Beispiel #1
0
class SessionService(QObject):
	session_started = Signal(SessionModel)
	session_paused = Signal(SessionModel)
	session_resumed = Signal(SessionModel)
	session_stopped = Signal()
	timer_updated = Signal(float)

	def __init__(self):
		super(SessionService, self).__init__()

		self.session_dao = SessionDao()

		self.time = 0
		self.state = SessionState.STOPPED

		self.timer = QTimer(self)
		self.timer.timeout.connect(self._timer_timeout)

		self.current_session = None

	def start_session(self, project):
		self.time = 0
		self.current_session = SessionModel(start = time.time(), project_id = project.id)
		self.current_session = self.session_dao.save(self.current_session)
		self.timer.start(1000)
		self.state = SessionState.ACTIVE
		self.session_started.emit(self.current_session)

	def pause_session(self):
		self.timer.stop()
		self.state = SessionState.PAUSED
		self.session_paused.emit(self.current_session)

	def resume_session(self):
		self.timer.start(1000)
		self.state = SessionState.ACTIVE
		self.current_pause = None
		self.session_resumed.emit(self.current_session)

	def stop_session(self):
		self.state = SessionState.STOPPED
		self.timer.stop()
		if self.current_session:
			self.current_session.end = time.time()
			self.current_session.duration = self.time
			self.session_dao.save(self.current_session)
			self.session_stopped.emit()
			self.current_session = None
		self.time = 0

	def get_state(self):
		return self.state

	def get_table_model(self, project):
		'Returns a table model with sessions for the specified project'
		return SessionTableModel(self, project, self.session_dao)

	def _timer_timeout(self):
		self.time += 1
		self.timer_updated.emit(self.time)
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 = Keithley6221_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()
        # 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 monitor(self, state=1):
        if state != 1:
            self.monitor_timer.stop()
        elif state and not (self.monitor_timer.isActive()):
            with self.reserved_access_to_instr:
                I = self.instr.query_current_source_amplitude()
                Vcomp = self.instr.query_voltage_compliance()
                outstate = self.instr.query_output_ON()
            self.ui.I_disp.setText(str(I * 1e6) + u" μA")
            self.ui.V_disp.setText(str(Vcomp) + " V")
            self.ui.outputON.setChecked(outstate)
            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_I(self, value=0):
        with self.reserved_access_to_instr:
            self.instr.set_current_source_amplitude(value * 1e6)

    def change_V_comp(self, value=0):
        with self.reserved_access_to_instr:
            self.instr.set_voltage_compliance(value)

    def switch_output(self, value=False):
        if value:
            with self.reserved_access_to_instr:
                self.instr.output_ON()
        else:
            with self.reserved_access_to_instr:
                self.instr.output_OFF()

    def reset_inst(self):
        with self.reserved_access_to_instr:
            self.instr.reset()
Beispiel #3
0
class PhotoListModel(QAbstractListModel):
    URL_ROLE = Qt.UserRole + 1
    IMAGE_URL_ROLE = Qt.UserRole + 2
    HEIGHT_ROLE = Qt.UserRole + 3
    WIDTH_ROLE = Qt.UserRole + 4

    def __init__(self, album, parent=None):
        super(PhotoListModel, self).__init__(parent)
        self._album = album
        self._photos = []
        self._cache = []
        self._done = False
        keys = {}
        keys[PhotoListModel.URL_ROLE] = "url"
        keys[PhotoListModel.IMAGE_URL_ROLE] = "imageUrl"
        keys[PhotoListModel.HEIGHT_ROLE] = "height"
        keys[PhotoListModel.WIDTH_ROLE] = "width"
        self.setRoleNames(keys)
        self._load = PhotoLoad(self)
        self._load.start()
        self._timer = QTimer()
        self._timer.setInterval(1000)
        self._timer.timeout.connect(self.loadCache)
        self._timer.start()

    def rowCount(self, index):
        return len(self._photos)

    def appendCache(self, itens):
        self._cache += itens

    def loadCache(self):
        self.beginInsertRows(QModelIndex(), len(self._photos),
                             len(self._photos) + len(self._cache))
        self._photos += self._cache
        self.endInsertRows()
        self._cache = []
        if self._done:
            self._timer.stop()

    def data(self, index, role):
        if not index.isValid():
            return None

        if index.row() > len(self._photos):
            return None

        img = self._photos[index.row()]
        if role == PhotoListModel.URL_ROLE:
            return img.url
        elif role == PhotoListModel.IMAGE_URL_ROLE:
            return img.imageUrl
        elif role == PhotoListModel.HEIGHT_ROLE:
            return img.height
        elif role == PhotoListModel.WIDTH_ROLE:
            return img.width
        else:
            return None
class PhotoListModel(QAbstractListModel):
    URL_ROLE = Qt.UserRole + 1
    IMAGE_URL_ROLE = Qt.UserRole + 2
    HEIGHT_ROLE = Qt.UserRole + 3
    WIDTH_ROLE = Qt.UserRole + 4

    def __init__(self, album, parent=None):
        super(PhotoListModel, self).__init__(parent)
        self._album = album
        self._photos = []
        self._cache = []
        self._done = False
        keys = {}
        keys[PhotoListModel.URL_ROLE] = "url"
        keys[PhotoListModel.IMAGE_URL_ROLE] = "imageUrl"
        keys[PhotoListModel.HEIGHT_ROLE] = "height"
        keys[PhotoListModel.WIDTH_ROLE] = "width"
        self.setRoleNames(keys)
        self._load = PhotoLoad(self)
        self._load.start()
        self._timer = QTimer()
        self._timer.setInterval(1000)
        self._timer.timeout.connect(self.loadCache)
        self._timer.start()

    def rowCount(self, index):
        return len(self._photos)

    def appendCache(self, itens):
        self._cache += itens

    def loadCache(self):
        self.beginInsertRows(QModelIndex(), len(self._photos), len(self._photos) + len(self._cache))
        self._photos += self._cache
        self.endInsertRows()
        self._cache = []
        if self._done:
            self._timer.stop()

    def data(self, index, role):
        if not index.isValid():
            return None

        if index.row() > len(self._photos):
            return None

        img = self._photos[index.row()]
        if role == PhotoListModel.URL_ROLE:
            return img.url
        elif role == PhotoListModel.IMAGE_URL_ROLE:
            return img.imageUrl
        elif role == PhotoListModel.HEIGHT_ROLE:
            return img.height
        elif role == PhotoListModel.WIDTH_ROLE:
            return img.width
        else:
            return None
Beispiel #5
0
class LogFilePositionSource(QGeoPositionInfoSource):
    def __init__(self, parent):
        QGeoPositionInfoSource.__init__(self, parent)
        self.logFile = QFile(self)
        self.timer = QTimer(self)

        self.timer.timeout.connect(self.readNextPosition)

        self.logFile.setFileName(translate_filename('simplelog.txt'))
        assert self.logFile.open(QIODevice.ReadOnly)

        self.lastPosition = QGeoPositionInfo()

    def lastKnownPosition(self, fromSatellitePositioningMethodsOnly):
        return self.lastPosition

    def minimumUpdateInterval(self):
        return 100

    def startUpdates(self):
        interval = self.updateInterval()

        if interval < self.minimumUpdateInterval():
            interval = self.minimumUpdateInterval()

        self.timer.start(interval)

    def stopUpdates(self):
        self.timer.stop()

    def requestUpdate(self, timeout):
        # For simplicity, ignore timeout - assume that if data is not available
        # now, no data will be added to the file later
        if (self.logFile.canReadLine()):
            self.readNextPosition()
        else:
            self.updateTimeout.emit()

    def readNextPosition(self):
        line = self.logFile.readLine().trimmed()

        if not line.isEmpty():
            data = line.split(' ')
            hasLatitude = True
            hasLongitude = True
            timestamp = QDateTime.fromString(str(data[0]), Qt.ISODate)
            latitude = float(data[1])
            longitude = float(data[2])
            if timestamp.isValid():
                coordinate = QGeoCoordinate(latitude, longitude)
                info = QGeoPositionInfo(coordinate, timestamp)
                if info.isValid():
                    self.lastPosition = info
                    # Currently segfaulting. See Bug 657
                    # http://bugs.openbossa.org/show_bug.cgi?id=657
                    self.positionUpdated.emit(info)
Beispiel #6
0
class LogFilePositionSource(QGeoPositionInfoSource):
    def __init__(self, parent):
        QGeoPositionInfoSource.__init__(self, parent)
        self.logFile = QFile(self)
        self.timer = QTimer(self)

        self.timer.timeout.connect(self.readNextPosition)

        self.logFile.setFileName(translate_filename('simplelog.txt'))
        assert self.logFile.open(QIODevice.ReadOnly)

        self.lastPosition = QGeoPositionInfo()

    def lastKnownPosition(self, fromSatellitePositioningMethodsOnly):
        return self.lastPosition

    def minimumUpdateInterval(self):
        return 100

    def startUpdates(self):
        interval = self.updateInterval()

        if interval < self.minimumUpdateInterval():
            interval = self.minimumUpdateInterval()

        self.timer.start(interval)

    def stopUpdates(self):
        self.timer.stop()

    def requestUpdate(self, timeout):
        # For simplicity, ignore timeout - assume that if data is not available
        # now, no data will be added to the file later
        if (self.logFile.canReadLine()):
            self.readNextPosition()
        else:
            self.updateTimeout.emit()

    def readNextPosition(self):
        line = self.logFile.readLine().trimmed()

        if not line.isEmpty():
            data = line.split(' ')
            hasLatitude = True
            hasLongitude = True
            timestamp = QDateTime.fromString(str(data[0]), Qt.ISODate)
            latitude = float(data[1])
            longitude = float(data[2])
            if timestamp.isValid():
                coordinate = QGeoCoordinate(latitude, longitude)
                info = QGeoPositionInfo(coordinate, timestamp)
                if info.isValid():
                    self.lastPosition = info
                    # Currently segfaulting. See Bug 657
                    # http://bugs.openbossa.org/show_bug.cgi?id=657
                    self.positionUpdated.emit(info)
Beispiel #7
0
class SyncThread(QThread):
    """Sync notes with evernote thread"""
    force_sync_signal = Signal()
    sync_state_changed = Signal(int)

    def __init__(self, app, *args, **kwargs):
        QThread.__init__(self, *args, **kwargs)
        self.app = app
        self.status = STATUS_NONE
        self.last_sync = datetime.now()
        self.timer = QTimer()
        self.timer.timeout.connect(self.sync)
        self.update_timer()
        self.wait_condition = QWaitCondition()
        self.mutex = QMutex()

    def update_timer(self):
        self.timer.stop()
        delay = int(self.app.settings.value('sync_delay') or 0) or DEFAULT_SYNC_DELAY
        if delay != SYNC_MANUAL:
            self.timer.start(delay)

    def run(self):
        self.session = get_db_session()
        self.sq = self.session.query
        self.auth_token = get_auth_token()
        self.note_store = get_note_store(self.auth_token)
        self.perform()
        while True:
            self.mutex.lock()
            self.wait_condition.wait(self.mutex)
            self.perform()
            self.mutex.unlock()

    def force_sync(self):
        self.timer.stop()
        self.sync()
        self.update_timer()

    @Slot()
    def sync(self):
        self.wait_condition.wakeAll()

    def perform(self):
        """Perform all sync"""
        self.status = STATUS_SYNC
        self.last_sync = datetime.now()
        self.sync_state_changed.emit(SYNC_STATE_START)
        try:
            self.local_changes()
            self.remote_changes()
        except Exception, e:  # maybe log this
            self.session.rollback()
        finally:
Beispiel #8
0
class FunnySpiral(QGLWidget):
    """
    Renders a spiral on a Qt widget.
    """

    def __init__(self, parent=None):
        QGLWidget.__init__(self, parent)
        self.timer = QTimer(self)
        # the timer, which drives the animation
        self.timer.timeout.connect(self.update)
        # the angle, by which the spiral is rotated
        self.angle = 0
        self.spiral = self.update_spiral()

    def start_rotation(self):
        self.timer.start(100)

    def stop_rotation(self):
        self.timer.stop()

    def update_spiral(self):
        # create a polygon providing the corner points of the spiral
        polygon = QPolygon()
        for i in xrange(self.window().width()):
            x = int(math.cos(i * 0.16) * i)
            y = int(math.sin(i * 0.16) * i)
            polygon.append(QPoint(x, y))
        return polygon

    def resizeEvent(self, evt):
        # re-create the spiral, if the widget is resized
        self.spiral = self.update_spiral()

    def paintEvent(self, evt):
        # create a painter
        with paint(self) as painter:
            # adjust the width of the pen
            pen = QPen()
            pen.setWidth(5)
            painter.setPen(pen)
            # enable high quality antialiasing
            painter.setRenderHint(QPainter.Antialiasing)
            painter.setRenderHint(QPainter.HighQualityAntialiasing)
            # move the center of the coordinate system to the widgets center
            painter.translate(self.width() / 2, self.height() / 2)
            # rotate the coordinate system by the given angle
            painter.rotate(self.angle)
            # draw the spiral
            painter.drawPolyline(self.spiral)
            # end painting and free resources

        # update the angle
        self.angle += 30
        self.angle %= 360
Beispiel #9
0
class Reader(QThread):
    data = Signal(Meter)
    warning = Signal(str)
    error = Signal(str)

    def __init__(self, port):
        QThread.__init__(self)

        self._port = port
        self._serial = None
        self._parser = Parser()
        self._cancel = False
        self._timeout = None

    def run(self):
        self._timeout = QTimer()
        self._timeout.setInterval(2000)
        self._timeout.timeout.connect(self.__onTimeout)

        try:
            self._serial = serial.Serial(self._port,
                                         baudrate=19200,
                                         bytesize=serial.SEVENBITS,
                                         stopbits=serial.STOPBITS_ONE,
                                         parity=serial.PARITY_ODD,
                                         timeout=1)
            self._serial.dtr = True
            self._serial.rts = False

            while not self._cancel:
                if not self._timeout.isActive():
                    self._timeout.start()
                data = self._serial.readline()
                data = data.strip()
                if len(data) == 12:
                    timestamp = time.time()
                    result = self._parser.parse(data, timestamp)
                    if result is not None:
                        self._timeout.stop()
                        self.data.emit(result)
                    else:
                        self.warning.emit('Invalid data received')
                QApplication.processEvents()
            self._serial.close()
        except serial.SerialException as e:
            self.error.emit(e.message)

    def __onTimeout(self):
        self.warning.emit('No data received')

    def stop(self):
        self._cancel = True
Beispiel #10
0
class Animation():
    _SetterGetter = 0; _SetAttr = 1;
    
    def __init__(self,obj,property_name_or_setter,getter=None):
        # decide how the property will be set
        if getter != None:
            self._setmethod = Animation._SetterGetter
            self._setter = property_name_or_setter; self._getter = getter
            
        else:
            self._propname = property_name_or_setter
            self._setmethod = Animation._SetAttr
            
        self._anim_obj = obj
        
        # initialize some variables
        self.startvalue = True
        self.endvalue = True
        self.duration = 1000
        self.interval = 5
        
        self.interpolator = True # the interpolator gives values for the animation
        self._previousvalue = QColor(0,0,0) # this is given to the interpolator in Animation.update()
        
        self.isvisual = True # this is used to tell whether to call QWidget.update() on self._anim_obj
        
        self._timer = QTimer() # this is used for timing the animation...duh
        self._timer.timeout.connect(self._update)
        
    def setBySetter(self,value): self._setter(value) 
    def setBySetAttr(self,value): setattr(self._anim_obj,self._propname,value)
    
    def start(self): 
        if type(self.startvalue) == QColor: # if we're working with colors, use a ColorInterpolator
            self.interpolator = ColorInterpolator(self.startvalue, self.endvalue, self.duration, self.interval, self.stop)
        else:
            raise NotImplementedError("There is no interpolator for ", str(type(self.start())))
        self._previousvalue = self.startvalue
        self._timer.start(self.interval)
        
    def _update(self):
        self._previousvalue = self.interpolator.getnextvalue(self._previousvalue)
        if self.isvisual: self._anim_obj.update()
        
    def stop(self):
        print "Stopped\n"
        self._timer.timeout.connect(self._blankmethod)
        self._timer.stop()
        
    def _blankmethod(self): print "blankmethod"
Beispiel #11
0
class InputHandler(observable.Object):
  def __init__(self, port, target):
    observable.Object.__init__(self)
    # store the source port and the target to send data to
    self._port = port
    self._target = target
    # add an idle timer to check for input
    self._timer = QTimer()
    self._timer.setInterval(0)
    self._timer.timeout.connect(self.receive)
    self._timer.start()
  def destroy(self):
    self._timer.timeout.disconnect(self.receive)
    self._timer.stop()
  @property
  def port(self):
    return(self._port)
  @property
  def target(self):
    return(self._target)
  # check for input
  def receive(self, limit_time=True):
    # limit processing time to maintain responsiveness
    time_limit = time.time() + 0.100
    # wrap the target in a change block so each midi event doesn't waste a lot
    #  of time causing cascading changes
    target_and_refs = (self._target,)
    try:
      target_and_refs += self._target.model_refs
    except AttributeError: pass
    for model in target_and_refs:
      try:
        model.begin_change_block()
      except AttributeError: pass
    while (True):
      result = self._port.receive()
      if (result is None): break
      (data, msg_time) = result
      self.handle_message(data, msg_time)
      # handle at least one message per run, but limit overall processing time
      #  to keep the UI responsive, allowing the jackpatch buffer to handle 
      #  the backlog
      if ((limit_time) and (time.time() > time_limit)): break
    for model in target_and_refs:
      try:
        model.end_change_block()
      except AttributeError: pass
  # handle input, reimplement to pass data to the target
  def handle_message(data, time):
    pass
Beispiel #12
0
 def downlaod(self, url, timeout=60):
     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_()
     if timer.isActive():
         timer.stop()
         return self.html()
     else:
         print 'Request timed out: ' + url
Beispiel #13
0
class Network(QObject):
    messageReceived = Signal(str)
    serverClosed = Signal()
    def __init__(self, parent):
        super(Network, self).__init__(parent)
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self._isConnected = False
        self.log = logger.Logger('client')
        self.threadRecv = None
        self.timerConnect = QTimer(self)
        self.timerConnect.timeout.connect(self.connect_)
        parent.updated.connect(self.slotUpdated)
    
    def state(self):
        return self._state
    
    def connect_(self):
        try:
            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.socket.connect((socket.gethostname(), cst.PORT))
            self._isConnected = True
            self.timerConnect.stop()
            self.threadRecv = ThreadRecv(self.socket, self.log)
            self.threadRecv.receivedMessage.connect(self.slotMessage)
            self.threadRecv.start()
            self.log.info('client connected')
            self.send('send-state')
        except:
            self._isConnected = False
            self.timerConnect.start(100)

    
    def slotMessage(self, msg):
        self.log.recv(msg)
        if msg == 'socket-closed':
            self._isConnected = False
            self.timerConnect.start(100)
            self.serverClosed.emit()
        else:
            self.messageReceived.emit(msg)
        
        
        
    def send(self, msg):
        if self._isConnected:
            try:
                self.socket.send(msg)
                self.log.send(msg)
            except socket.error, msg:
                self.log.warning('sending failed: %s', msg)
Beispiel #14
0
class HeartBeatTimer(QObject):
    """ Class for HeartBeat event timer
    """
    __status_changed = Signal(str)

    def __init__(self, period, callback):
        """ Constructor for HeartBeatTimer class

        :param int period: timeout period in msec
        :param callback: callback function to be called when timer changes its status
        callback signature: callback(status)
        """
        super(HeartBeatTimer, self).__init__()
        self.__period = period
        self.is_alive = False
        self.__timer = QTimer(self)
        self.__timer.timeout.connect(self.__heartbeat_timer_expired)
        self.__timer.start(self.__period)
        self.__status_changed.connect(callback)
        self.__status_changed.emit("OFFLINE")

    def refresh_timer(self):
        """ Restart the heartbeat timer

        """
        self.__timer.start(self.__period)
        if not self.is_alive:
            self.is_alive = True
            self.__status_changed.emit("ONLINE")

    def change_timeout_period(self, period):
        """ Change the existing timeout period if not same

        :param int period: new timeout period in msec
        """
        if period != self.__period:
            self.__period = period
            self.__timer.start(self.__period)

    @Slot()
    def __heartbeat_timer_expired(self):
        """ Slot function for heartbeat timeout event

        """
        self.__timer.stop()
        if self.is_alive:
            self.is_alive = False
            self.__status_changed.emit("OFFLINE")
class HeartBeatTimer(QObject):
    """ Class for HeartBeat event timer
    """
    __status_changed = Signal(str)

    def __init__(self, period, callback):
        """ Constructor for HeartBeatTimer class

        :param int period: timeout period in msec
        :param callback: callback function to be called when timer changes its status
        callback signature: callback(status)
        """
        super(HeartBeatTimer, self).__init__()
        self.__period = period
        self.is_alive = False
        self.__timer = QTimer(self)
        self.__timer.timeout.connect(self.__heartbeat_timer_expired)
        self.__timer.start(self.__period)
        self.__status_changed.connect(callback)
        self.__status_changed.emit("OFFLINE")

    def refresh_timer(self):
        """ Restart the heartbeat timer

        """
        self.__timer.start(self.__period)
        if not self.is_alive:
            self.is_alive = True
            self.__status_changed.emit("ONLINE")

    def change_timeout_period(self, period):
        """ Change the existing timeout period if not same

        :param int period: new timeout period in msec
        """
        if period != self.__period:
            self.__period = period
            self.__timer.start(self.__period)

    @Slot()
    def __heartbeat_timer_expired(self):
        """ Slot function for heartbeat timeout event

        """
        self.__timer.stop()
        if self.is_alive:
            self.is_alive = False
            self.__status_changed.emit("OFFLINE")
Beispiel #16
0
class Timer(QObject):
	onTimer=None

	def __init__(self, interval):
		super(Timer, self).__init__()
		self.timer = QTimer(self)
		self.timer.timeout.connect(self.handleTimer)
		self.timer.start(interval)

	def stop(self):
		self.timer.stop()

	@Slot()
	def handleTimer(self):
		if self.onTimer:
			self.onTimer()
Beispiel #17
0
class WidgetLed(QWidget):

    def __init__(self, parent, colour='#000000'):
        QWidget.__init__(self, parent)
        self._colour = QColor(colour)

        self.setMinimumSize(20, 20)
        self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        self._lit = False
        self._timer = QTimer(self)
        self._timer.setInterval(200)
        self._timer.timeout.connect(self.__flash_off)

    def paintEvent(self, event):
        painter = QPainter()
        painter.begin(self)

        colour = self._colour
        if not self._lit:
            colour = self._colour.darker(300)
        painter.setPen(QPen(Qt.black, 1))
        painter.setBrush(QBrush(colour))

        rect = event.rect()
        radius = min(rect.width(), rect.height()) / 3
        painter.drawEllipse(rect.center(), radius, radius)

        painter.end()

    def __flash_off(self):
        self._timer.stop()
        self._lit = False
        self.repaint()

    def flash(self):
        self._lit = True
        self._timer.start()
        self.repaint()

    def light(self, on):
        self._timer.stop()
        self._lit = on
        self.repaint()

    def set_colour(self, colour):
        self._colour = QColor(colour)
Beispiel #18
0
class Browser(QWidget):
    def __init__(self):
        super(Browser, self).__init__()

        self.layout = QStackedLayout(self)
        self.layout.setStackingMode(QStackedLayout.StackAll)

        self.hostname = os.uname()[1]

        self.timer = QTimer()
        self.timer.setSingleShot(False)
        self.timer.start(2000)

        self._init_sites()

        self.remote_shell = RemoteShell(self)

        self.connect(self.timer, SIGNAL("timeout()"), self, SLOT("show_next()"))
        self.show_next()

    def _init_sites(self):
        self.sites = list()
        self.site_id = -1

        url = QUrl("https://www.qt.io/developers/")
        self.sites.append(Site(self, url, 5, 1))

        url = QUrl("https://wiki.qt.io/PySide")
        self.sites.append(Site(self, url, 5, 1))

        url = QUrl("https://www.python.org/")
        self.sites.append(Site(self, url, 5, 1))

    @property
    def current_site(self):
        return self.sites[self.site_id]

    def show_next(self):
        self.timer.stop()
        previous_id = self.site_id
        self.site_id = (self.site_id + 1) % len(self.sites)
        current_site = self.current_site
        self.timer.start(current_site.time * 1000)
        current_site.show()
        print("show " + current_site.url.toString().encode())
        if previous_id >= 0:
            self.sites[previous_id].hide()
Beispiel #19
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 finished
     if timer.isActive():
         # downloaded successfully
         timer.stop()
         return self.html()
     else:
         # timed out
         print 'Request timed out:', url
class _VideoComponentEditor(_LaserComponentEditor):
    """
    """
    playTimer = Any
    fps = Int
    stop_timer = Event

    def init(self, parent):
        """
        Finishes initializing the editor by creating the underlying toolkit
        widget.

        """
        super(_VideoComponentEditor, self).init(parent)

        self.playTimer = QTimer(self.control)
        # self.playTimer.timeout.connect(self.update)
        self.control.connect(self.playTimer, SIGNAL('timeout()'), self.update)

        if self.value.fps:
            self.playTimer.setInterval(1000 / float(self.value.fps))
        self.playTimer.start()
        self.value.on_trait_change(self.stop, 'closed_event')

        self.value.on_trait_change(self._update_fps, 'fps')
        self.sync_value('stop_timer', 'stop_timer', mode='from')

    def _update_fps(self):
        if self.value.fps:
            self.playTimer.setInterval(1000 / float(self.value.fps))

    def stop(self):
        print 'VideoComponentEditor stop'
        try:
            self.playTimer.stop()
        except RuntimeError:
            pass

    def update(self):
        if self.control:
            # invoke_in_main_thread(self.value.request_redraw)
            self.value.request_redraw()

    def _stop_timer_fired(self):
        print 'VideoComponentEditor stopping playTimer'
        self.playTimer.stop()
Beispiel #21
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 finished
     if timer.isActive():
         # downloaded successfully
         timer.stop()
         return self.html()
     else:
         # timed out
         print 'Request timed out:', url
Beispiel #22
0
    def open(self, url, timeout=60):
        """wait for download to complete and return result"""
        loop = QEventLoop()
        timer = QTimer()
        timer.setSingleShot(True)  # True表示触发定时器后,仅执行事件一次
        timer.timeout.connect(loop.quit)  # 若超时,则连接loop.quit,退出事件循环
        self.loadFinished.connect(loop.quit)
        self.load(url)
        timer.start(timeout * 1000)  # 定时器以ms为单位,设置超时时间为60s
        loop.exec_()  # 等待网页加载完成后,在执行后面的代码

        if timer.isActive():
            # downloaded successfully
            timer.stop()
            return self.html()
        else:
            # timed out
            print 'Request timed out:', url
class _VideoComponentEditor(_LaserComponentEditor):
    """
    """

    playTimer = Any
    fps = Int
    stop_timer = Event

    def init(self, parent):
        """
        Finishes initializing the editor by creating the underlying toolkit
        widget.

        """
        super(_VideoComponentEditor, self).init(parent)

        self.playTimer = QTimer(self.control)
        self.playTimer.timeout.connect(self.update)
        if self.value.fps:
            self.playTimer.setInterval(1000 / self.value.fps)
        self.playTimer.start()
        self.value.on_trait_change(self.stop, "closed_event")

        self.value.on_trait_change(self._update_fps, "fps")
        self.sync_value("stop_timer", "stop_timer", mode="both")

    def _update_fps(self):
        if self.value.fps:
            self.playTimer.setInterval(1000 / self.value.fps)

    def stop(self):
        try:
            self.playTimer.stop()
        except RuntimeError:
            del self.playTimer

    def update(self):
        if self.control:
            self.value.draw_valid = False
            self.control.repaint()

    def _stop_timer_fired(self):
        print "VideoComponentEditor stopping playTimer"
        self.playTimer.stop()
Beispiel #24
0
class WidgetLed(QWidget):
    def __init__(self, parent, colour='#000000'):
        QWidget.__init__(self, parent)
        self._colour = QColor(colour)

        self.setMinimumSize(10, 10)

        self._lit = False
        self._timer = QTimer(self)
        self._timer.setInterval(200)
        self._timer.timeout.connect(self.__flashOff)

    def paintEvent(self, event):
        painter = QPainter()
        painter.begin(self)

        if self._lit:
            self._colour.setAlphaF(1)
        else:
            self._colour.setAlphaF(.25)
        painter.setPen(QPen(self._colour, 1))
        painter.setBrush(QBrush(self._colour))

        rect = event.rect()
        radius = min(rect.width(), rect.height()) / 3
        painter.drawEllipse(rect.center(), radius, radius)

        painter.end()

    def __flashOff(self):
        self._timer.stop()
        self._lit = False
        self.repaint()

    def flash(self):
        self._lit = True
        self._timer.start()
        self.repaint()

    def light(self, on):
        self._timer.stop()
        self._lit = on
        self.repaint()
Beispiel #25
0
class MainWindow(QMainWindow):
    def __init__(self, opc=None):
        super(MainWindow, self).__init__()

        self.opc = opc

        self.setWindowTitle('OPC DA Datalogger')

        self.layout = QHBoxLayout(self)

        self.configuration = Configuration(self, opc)
        self.configuration_dock = QDockWidget("Configuration", self)
        self.configuration_dock.setWidget(self.configuration)
        self.configuration_dock.setFeatures(QDockWidget.NoDockWidgetFeatures)
        self.addDockWidget(Qt.LeftDockWidgetArea, self.configuration_dock)

        self.logging_area = LoggingArea(self)
        self.logging_dock = QDockWidget("Logging", self)
        self.logging_dock.setWidget(self.logging_area)
        self.logging_dock.setFeatures(QDockWidget.NoDockWidgetFeatures)
        self.addDockWidget(Qt.RightDockWidgetArea, self.logging_dock)

        self.setLayout(self.layout)
        self.resize(900, 600)

        self.logging_timer = QTimer(self)
        self.logging_timer.timeout.connect(self._loogging_callback)

    def _start_logging(self):
        self.tags = self.configuration.tag_selection.selected_tags()
        header, line = opcda.read(self.opc, self.tags)
        self.logging_area.te_logging.clear()
        self.logging_area.te_logging.append(header)
        self.logging_timer.start(500)

    def _loogging_callback(self):
        header, line = opcda.read(self.opc, self.tags)
        self.logging_area.te_logging.append(line)

    def _stop_logging(self):
        self.logging_timer.stop()
class AntReverseCalculation():
    """
    Callable function calls every second and at once, and it must has "seconds_before_start" as argument.
    """
    def __init__(self):
        self._seconds_before_start = 0
        self._timer = QTimer()
        QObject.connect(self._timer, SIGNAL('timeout()'), self._action)

    def _action(self):
        self._seconds_before_start -= 1
        if self._seconds_before_start == 0:
            self._timer.stop()

        self._callable(seconds_before_start=self._seconds_before_start)

    def startReverseCalculation(self, number_of_seconds, callable_function):
        self._callable = callable_function
        self._seconds_before_start = copy(number_of_seconds) + 1
        self._timer.start(1000)
        self._action()
Beispiel #27
0
class CountdownWidget(QLabel):
    def __init__(self, countdown, parent=None):
        QLabel.__init__(self, parent)
        self.countdown = countdown
        self.setText(self.countdown.toString(Qt.ISODate))
        # setup the countdown timer
        self.timer = QTimer(self)
        self.timer.timeout.connect(self._update_time)

    def start(self):
        # update the display every second
        self.timer.start(1000)

    def _update_time(self):
        # this gets called every seconds
        # adjust the remaining time
        self.countdown = self.countdown.addSecs(-1)
        # if the remaining time reached zero, stop the timer
        if self.countdown <= QTime(0, 0, 0):
            self.timer.stop()
        # update the display
        self.setText(self.countdown.toString(Qt.ISODate))
Beispiel #28
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()
        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()
Beispiel #29
0
class CountdownWidget(QLabel):
    def __init__(self, countdown, parent=None):
        QLabel.__init__(self, parent)
        self.countdown = countdown
        self.setText(self.countdown.toString(Qt.ISODate))
        # setup the countdown timer
        self.timer = QTimer(self)
        self.timer.timeout.connect(self._update_time)

    def start(self):
        # update the display every second
        self.timer.start(1000)

    def _update_time(self):
        # this gets called every seconds
        # adjust the remaining time
        self.countdown = self.countdown.addSecs(-1)
        # if the remaining time reached zero, stop the timer
        if self.countdown <= QTime(0, 0, 0):
            self.timer.stop()
        # update the display
        self.setText(self.countdown.toString(Qt.ISODate))
Beispiel #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()
        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()
Beispiel #31
0
class QtOneSecondTimer(OneSecondTimer):
    """
    Concrete implementation of the timer based on Qt classes
    """

    def __init__(self):
        OneSecondTimer.__init__(self)
        self.__timer = QTimer()
        self.__timer.setInterval(1000)
        self.__timer.setSingleShot(False)
        self.__timer.timeout.connect(self._timerElapsed)

    def start(self):
        """Start the timer"""
        self.__timer.start()

    def _timerElapsed(self):
        """Process notification from the timer"""
        self.elapsed.fire()

    def stop(self):
        """Stop the timer"""
        self.__timer.stop()
Beispiel #32
0
class Quiz(QFrame):
    def __init__(self, parent=None):
        super(Quiz, self).__init__(parent)
        """Session Info"""
        self.status = QFrame()
        ##session message
        self.status.message = QLabel(u'')
        self.status.layout = QHBoxLayout()
        self.status.layout.addWidget(self.status.message)
        self.status.setLayout(self.status.layout)
        ##mouse event filter
        #self.status.filter = StatusFilter()
        self.status.setAttribute(Qt.WA_Hover, True)
        #self.status.installEventFilter(self.status.filter)
        """Items Info"""
        self.info = QFrame()
        self.info.reading = QLabel(u'')
        self.info.item = QLabel(u'')
        self.info.components = QLabel(u'')

        self.info.translation = QLabel(u'')
        self.info.translation.setAlignment(Qt.AlignCenter)
        self.info.translation.setWordWrap(True)

        #        separator_one = QFrame()
        #        separator_one.setFrameShape(QFrame.HLine)
        #        separator_one.setFrameShadow(QFrame.Sunken)
        #
        #        separator_two = QFrame()
        #        separator_two.setFrameShape(QFrame.HLine)
        #        separator_two.setFrameShadow(QFrame.Sunken)

        self.info.layout = QVBoxLayout()
        self.info.layout.addWidget(self.info.translation)
        #        self.info.layout.addWidget(self.info.reading)
        #        self.info.layout.addWidget(separator_one)
        #        self.info.layout.addWidget(self.info.item)
        #        self.info.layout.addWidget(separator_two)
        #        self.info.layout.addWidget(self.info.components)
        self.info.setLayout(self.info.layout)
        """Verbose Info"""
        self.allInfo = QFrame()
        self.allInfo.layout = QGridLayout()
        self.allInfo.setLayout(self.allInfo.layout)
        #the rest is (should be) generated on the fly
        """Global Flags"""
        #self.correct = False
        """Quiz Dialog"""
        self.filter = Filter()
        ####    visual components    ###
        self.countdown = QProgressBar()
        self.sentence = QLabel(u'')

        #        self.var_1st = QPushButton(u'')
        #        self.var_2nd = QPushButton(u'')
        #        self.var_3rd = QPushButton(u'')
        #        self.var_4th = QPushButton(u'')

        self.isKnown = QPushButton(u'Good')
        self.isNotKnown = QPushButton(u'Again')

        self.answered = QPushButton(u'')
        self.answered.hide()

        ###    layouts    ####
        self.layout_vertical = QVBoxLayout()  #main
        self.layout_horizontal = QHBoxLayout()  #buttons

        #        self.layout_horizontal.addWidget(self.var_1st)
        #        self.layout_horizontal.addWidget(self.var_2nd)
        #        self.layout_horizontal.addWidget(self.var_3rd)
        #        self.layout_horizontal.addWidget(self.var_4th)

        self.layout_horizontal.addWidget(self.isKnown)
        self.layout_horizontal.addWidget(self.isNotKnown)

        self.layout_vertical.addWidget(self.countdown)
        self.layout_vertical.addWidget(self.sentence)
        self.layout_vertical.addLayout(self.layout_horizontal)

        self.layout_horizontal.addWidget(self.answered)

        self.setLayout(self.layout_vertical)

        ###    utility components    ###
        self.trayIcon = QSystemTrayIcon(self)
        self.trayMenu = QMenu()

        self.gifLoading = QMovie('../res/cube.gif')
        self.gifLoading.frameChanged.connect(self.updateTrayIcon)

        self.nextQuizTimer = QTimer()
        self.nextQuizTimer.setSingleShot(True)
        self.nextQuizTimer.timeout.connect(self.showQuiz)

        self.countdownTimer = QTimer()
        self.countdownTimer.setSingleShot(True)
        self.countdownTimer.timeout.connect(self.timeIsOut)

        ### initializing ###
        self.initializeResources()
        """Start!"""
        if self.options.isQuizStartingAtLaunch():
            self.waitUntilNextTimeslot()
            self.trayIcon.setToolTip('Quiz has started automatically!')
            self.pauseAction.setText('&Pause')
            self.trayIcon.showMessage(
                'Loading complete! (took ~' + str(self.loadingTime.seconds) +
                ' seconds) Quiz underway.', 'Lo! Quiz already in progress!',
                QSystemTrayIcon.MessageIcon.Warning, 10000)
        else:
            self.trayIcon.setToolTip('Quiz is not initiated!')
            self.trayIcon.showMessage(
                'Loading complete! (took ~' + str(self.loadingTime.seconds) +
                ' seconds) Standing by.',
                'Quiz has not started yet! If you wish, you could start it manually or enable autostart by default.',
                QSystemTrayIcon.MessageIcon.Information, 10000)
        """Test calls here:"""
        ###    ...    ###
        #self.connect(self.hooker, SIGNAL('noQdict'), self.noQdict)

    def noQdict(self):
        self.showSessionMessage(
            'Nope, cannot show quick dictionary during actual quiz.')

####################################
#    Initialization procedures     #
####################################

    def initializeResources(self):
        """Pre-initialization"""
        self.animationTimer = ()
        self.progressTimer = ()
        self.grid_layout = ()
        """Initialize Options"""
        self.options = Options()
        """Initialize Statistics"""
        self.stats = Stats()
        """Config Here"""
        self.initializeComposition()
        self.initializeComponents()
        self.setMenus()
        self.trayIcon.show()
        #self.startTrayLoading()
        """"Initialize Dictionaries    (will take a some time!)"""
        time_start = datetime.now()
        self.dict = EdictParser()
        self.dict.loadDict()

        self.morphy = get_morph(PATH_TO_RES + DICT_EN)

        self.trayIcon.showMessage(
            'Loading...', 'Initializing dictionaries',
            QSystemTrayIcon.MessageIcon.Information,
            20000)  #TODO: change into loading dialog... or not
        """Initializing srs system"""
        self.trayIcon.showMessage('Loading...', 'Initializing databases',
                                  QSystemTrayIcon.MessageIcon.Information,
                                  20000)
        self.srs = srsScheduler()
        self.srs.initializeAll()
        self.srs.initializeCurrentSession(self.options.getSessionSize())
        """Global hotkeys hook"""
        #TODO: add multiple hotkeys and fix stop()
        #self.hooker = GlobalHotkeyManager(toggleQDictFlag, 'Q')
        #        self.hooker = GlobalHotkeyManager(toggleWidgetFlag(self.qdict), 'Q')
        #        self.hooker.setDaemon(True) #temporarily, should work using stop()
        #        self.hooker.start()

        time_end = datetime.now()
        self.loadingTime = time_end - time_start

####################################
#    Composition and appearance    #
####################################

    def initializeComposition(self):
        """Main Dialog"""
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.setFocusPolicy(Qt.StrongFocus)
        self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        #Font will appear in buttons
        self.setFont(
            QFont(self.options.getQuizFont(), self.options.getQuizFontSize()))

        desktop = QApplication.desktop().screenGeometry()
        self.setGeometry(
            QRect(desktop.width() - H_INDENT,
                  desktop.height() - V_INDENT, D_WIDTH, D_HEIGHT))

        self.setStyleSheet("QWidget { background-color: rgb(255, 255, 255); }")
        """Info dialog"""
        self.info.setWindowFlags(Qt.FramelessWindowHint
                                 | Qt.WindowStaysOnTopHint)
        self.info.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        self.info.setGeometry(
            QRect(desktop.width() - H_INDENT - I_WIDTH - I_INDENT,
                  desktop.height() - V_INDENT, I_WIDTH, I_HEIGHT))
        self.info.setFixedSize(I_WIDTH, I_HEIGHT)

        self.info.setStyleSheet(
            "QWidget { background-color: rgb(255, 255, 255); }")
        """Verbose info dialog"""
        self.allInfo.setWindowFlags(Qt.FramelessWindowHint
                                    | Qt.WindowStaysOnTopHint)
        self.allInfo.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        self.allInfo.setGeometry(
            QRect(desktop.width() - H_INDENT - I_WIDTH - I_INDENT,
                  desktop.height() - V_INDENT, I_WIDTH, I_HEIGHT))

        self.allInfo.setStyleSheet(
            "QWidget { background-color: rgb(255, 255, 255); }")
        """Session message"""
        self.status.setWindowFlags(Qt.FramelessWindowHint
                                   | Qt.WindowStaysOnTopHint)
        self.status.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
        self.status.setGeometry(
            QRect(
                desktop.width() - H_INDENT,
                desktop.height() - V_INDENT - S_HEIGHT - S_INDENT -
                S_CORRECTION, S_WIDTH, S_HEIGHT))

        self.status.setStyleSheet(
            "QWidget { background-color: rgb(255, 255, 255); }")

        self.setMask(roundCorners(self.rect(), 5))
        self.status.setMask(roundCorners(self.status.rect(), 5))
        #self.info.setMask(roundCorners(self.info.rect(),5))
        #self.allInfo.setMask(roundCorners(self.allInfo.rect(),5))

    def initializeComponents(self):
        self.countdown.setMaximumHeight(6)
        self.countdown.setRange(0, self.options.getCountdownInterval() * 100)
        self.countdown.setTextVisible(False)
        self.countdown.setStyleSheet(
            "QProgressbar { background-color: rgb(255, 255, 255); }")

        #self.setFont(QFont(Fonts.SyoutyouProEl, 40))#self.options.getQuizFontSize()))

        self.sentence.setAlignment(Qt.AlignmentFlag.AlignCenter)
        #self.sentence.setFont(QFont(Fonts.HiragiNoMarugotoProW4, self.options.getSentenceFontSize()))
        self.sentence.setFont(
            QFont(self.options.getSentenceFont(),
                  self.options.getSentenceFontSize()))

        self.sentence.setWordWrap(True)
        self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'active.png'))

        self.status.message.setFont(
            QFont('Cambria', self.options.getMessageFontSize()))
        self.status.layout.setAlignment(Qt.AlignCenter)
        self.status.message.setWordWrap(False)
        self.status.layout.setMargin(0)

        self.info.item.setFont(QFont(Fonts.HiragiNoMyoutyouProW3, 36))
        self.info.reading.setFont(QFont(Fonts.HiragiNoMyoutyouProW3, 16))
        self.info.components.setFont((QFont(Fonts.HiragiNoMyoutyouProW3, 14)))
        #self.info.item.setWordWrap(True)
        self.info.components.setWordWrap(True)
        #self.info.layout.setAlignment(Qt.AlignCenter)
        self.info.layout.setMargin(0)
        #self.info.layout.setSizeConstraint(self.info.layout.SetFixedSize)       #NB: would work nice, if the anchor point was in right corner

        self.info.reading.setAlignment(Qt.AlignCenter)
        self.info.item.setAlignment(Qt.AlignCenter)
        self.info.components.setAlignment(Qt.AlignCenter)

        #self.info.setLayoutDirection(Qt.RightToLeft)

        self.info.reading.setStyleSheet(
            "QLabel { color: rgb(155, 155, 155); }")
        self.info.components.setStyleSheet(
            "QLabel { color: rgb(100, 100, 100); }")

        self.info.gem = self.info.saveGeometry()

####################################
#        Updating content          #
####################################

    def updateContent(self):
        """Resetting multi-label sentence"""
        if self.grid_layout != ():
            for i in range(0, self.grid_layout.count()):
                self.grid_layout.itemAt(i).widget().hide()
            self.layout_vertical.removeItem(self.grid_layout)
            self.grid_layout.setParent(None)
            self.update()
        if self.sentence.isHidden():
            self.sentence.show()
        self.showButtonsQuiz()
        """Getting actual content"""
        self.srs.getNextItem()

        start = datetime.now()  #testing
        #example = self.srs.getCurrentExample().replace(self.srs.getWordFromExample(), u"<font color='blue'>" + self.srs.getWordFromExample() + u"</font>")
        example = self.srs.getCurrentExample().replace(
            self.srs.getCurrentItem(),
            u"<font color='blue'>" + self.srs.getCurrentItem() + u"</font>")
        print datetime.now() - start  #testing
        self.sentence.setText(example)

        #        start = datetime.now()  #testing
        #        readings = self.srs.getQuizVariants()
        #        print datetime.now() - start    #testing
        '''
        changeFont = False
        for item in readings:
            if len(item) > 5 : changeFont = True
            
        if changeFont: self.setStyleSheet('QWidget { font-size: 11pt; }')
        else:   self.setStyleSheet('QWidget { font-size: %spt; }' % self.options.getQuizFontSize())
        '''
        '''
        if len(readings) == 4:                  #NB: HERE LIES THE GREAT ERROR
            self.var_1st.setText(readings[0])
            self.var_2nd.setText(readings[1])
            self.var_3rd.setText(readings[2])
            self.var_4th.setText(readings[3])
        '''

#        try:
#            for i in range(0, self.layout_horizontal.count()):
#                    if i > 3: break
#                    self.layout_horizontal.itemAt(i).widget().setText(u'')
#                    #self.layout_horizontal.itemAt(i).setStyleSheet('QPushButton { font-size: 11pt; }')
#                    self.layout_horizontal.itemAt(i).widget().setText(readings[i])
#        except:
#            print 'Not enough quiz variants'
#TODO: log this

    def getReadyPostLayout(self):
        self.sentence.hide()
        self.update()

        self.grid_layout = QGridLayout()
        self.grid_layout.setSpacing(0)
        self.labels = []

        columns_mod = 0

        if len(self.srs.currentExample['eng']) > SENTENCE_MAX:
            font = QFont(self.options.getSentenceFont(), MIN_FONT_SIZE)
            columns_mod = 6
        else:
            font = QFont(self.options.getSentenceFont(),
                         self.options.getSentenceFontSize())

        #row, column, rows span, columns span, max columns
        i = 0
        j = 0
        r = 1
        c = 1
        n = COLUMNS_MAX + columns_mod
        for word in self.srs.parseCurrentExample():
            label = QLabel(word)
            #            label.setFont(QFont(self.options.getSentenceFont(), self.options.getSentenceFontSize()))
            label.setFont(font)

            label.setAttribute(Qt.WA_Hover, True)
            label.installEventFilter(self.filter)
            self.labels.append(label)

            if len(label.text()) > 1: c = len(label.text())
            else: c = 1
            #Don't ask, really
            if j + c > n:
                i = i + 1
                j = 0

            self.grid_layout.addWidget(self.labels.pop(), i, j, r, c)

            if j <= n: j = j + c
            else:
                j = 0
                i = i + 1

        self.grid_layout.setAlignment(Qt.AlignCenter)
        self.layout_vertical.insertLayout(1, self.grid_layout)

        self.update()

    def hideButtonsQuiz(self):
        self.isKnown.hide()
        self.isNotKnown.hide()

        self.answered.clicked.connect(self.hideQuizAndWaitForNext)
        self.answered.show()

    def showButtonsQuiz(self):
        self.isKnown.show()
        self.isNotKnown.show()

        self.answered.hide()
        self.answered.disconnect()

####################################
#        Timers and animations     #
####################################

    def waitUntilNextTimeslot(self):
        #if self.nextQuizTimer.isActive():   self.nextQuizTimer.stop()
        self.nextQuizTimer.start(
            self.options.getRepetitionInterval() * 60 * 1000
        )  #options are in minutes    NB: how do neatly I convert minutes to ms?

    def beginCountdown(self):
        self.trayIcon.setToolTip('Quiz in progress!')
        self.pauseAction.setText('&Pause')
        self.pauseAction.setShortcut('P')

        self.countdownTimer.start(self.options.getCountdownInterval() * 1000)

        self.progressTimer = RepeatTimer(
            0.01, self.updateCountdownBar,
            self.options.getCountdownInterval() * 100)
        self.progressTimer.start()

    def updateCountdownBar(self):
        self.countdown.setValue(self.countdown.value() - 1)
        #print self.countdown.value()
        self.countdown.update(
        )  #NB: without .update() recursive repaint crushes qt

    def fade(self):
        if self.windowOpacity() == 1:
            self.animationTimer = RepeatTimer(0.025, self.fadeOut, 40)
            self.animationTimer.start()
        else:
            self.animationTimer = RepeatTimer(0.025, self.fadeIn, 40)
            self.animationTimer.start()

    def fadeIn(self):
        self.setWindowOpacity(self.windowOpacity() + 0.1)

    def fadeOut(self):
        self.setWindowOpacity(self.windowOpacity() - 0.1)

    def stopCountdown(self):
        self.progressTimer.cancel()
        self.countdownTimer.stop()
        self.countdown.setValue(0)

####################################
#        Actions and events        #
####################################

    def setMenus(self):
        self.trayMenu.addAction(
            QAction('&Quiz me now!',
                    self,
                    shortcut="Q",
                    triggered=self.showQuiz))
        self.pauseAction = QAction('&Start quiz!',
                                   self,
                                   shortcut="S",
                                   triggered=self.pauseQuiz)
        self.trayMenu.addAction(self.pauseAction)
        self.trayMenu.addSeparator()
        self.trayMenu.addAction(
            QAction('Quick &dictionary',
                    self,
                    shortcut="D",
                    triggered=self.showQuickDict))
        self.trayMenu.addAction(
            QAction('&Global &statistics',
                    self,
                    shortcut="G",
                    triggered=self.showGlobalStatistics))
        self.trayMenu.addAction(
            QAction('&Options', self, shortcut="O",
                    triggered=self.showOptions))
        self.trayMenu.addAction(
            QAction('&About', self, shortcut="A", triggered=self.showAbout))
        self.trayMenu.addSeparator()
        self.trayMenu.addAction(
            QAction('&Exit', self, shortcut="E", triggered=self.saveAndExit))

        self.trayIcon.setContextMenu(self.trayMenu)
        self.trayIcon.activated.connect(self.onTrayIconActivated)

    #TODO: show session statistics
    def onTrayIconActivated(self, reason):
        '''
        if reason == QSystemTrayIcon.DoubleClick:
            print 'tray icon double clicked'
        '''
        if reason == QSystemTrayIcon.Trigger:
            if self.isHidden():
                self.trayIcon.showMessage(
                    'Current session statistics:',
                    'Running time:\t\t' + self.stats.getRunningTime() +
                    '\nItems seen:\t\t' + str(self.stats.totalItemSeen) +
                    '\nCorrect answers:\t\t' +
                    str(self.stats.answeredCorrect) + '\nWrong answers:\t\t' +
                    self.stats.getIncorrectAnswersCount() +
                    '\nCorrect ratio:\t\t' +
                    self.stats.getCorrectRatioPercent() +
                    #'\nQuiz total time:\t\t' + self.stats.getQuizActive() +
                    '\nQuiz paused time:\t\t' + self.stats.getPausedTime() +
                    '\nTotal pondering time:\t' + self.stats.getMusingsTime() +
                    '\nTotal post-quiz time:\t' + self.stats.getQuizTime() +
                    '\nAverage pondering:\t' +
                    self.stats.getAverageMusingTime() +
                    '\nAverage post-quiz:\t' +
                    self.stats.getAveragePostQuizTime(),
                    QSystemTrayIcon.MessageIcon.Information,
                    20000)

    def setButtonsActions(self):
        self.isKnown.clicked.connect(self.correctAnswer)
        self.isNotKnown.clicked.connect(self.wrongAnswer)

    def resetButtonsActions(self):
        self.isKnown.disconnect()
        self.isNotKnown.disconnect()

    def postAnswerActions(self):
        self.stats.musingsStopped()
        self.stats.postQuizStarted()

        self.stopCountdown()
        self.hideButtonsQuiz()

        self.getReadyPostLayout()

    def checkTranslationSize(self, translation):
        if len(translation) > TRANSLATION_CHARS_LIMIT:
            self.answered.setStyleSheet('QPushButton { font-size: 9pt; }')

            space_indices = [
                i for i, value in enumerate(translation) if value == ' '
            ]
            find_nearest_index = lambda value, list: min(
                list, key=lambda x: abs(x - value))
            nearest_index = find_nearest_index(TRANSLATION_CHARS_LIMIT,
                                               space_indices)
            translation = translation[:nearest_index] + '\n' + translation[
                nearest_index + 1:]
        else:
            self.answered.setStyleSheet('QPushButton { font-size: 11pt; }')

        self.answered.setText(translation)

    def correctAnswer(self):
        '''
        self.stats.musingsStopped()
        self.stats.postQuizStarted()
        
        self.stopCountdown()
        self.hideButtonsQuiz()
        
        self.getReadyPostLayout()
        '''
        self.postAnswerActions()

        self.srs.answeredCorrect()
        self.stats.quizAnsweredCorrect()
        #self.answered.setText(u"<font='Cambria'>" + self.srs.getCurrentSentenceTranslation() + "</font>")
        #        self.answered.setText(self.srs.getCurrentSentenceTranslation())

        self.checkTranslationSize(self.srs.getCurrentSentenceTranslation())

        #self.answered.setFont(QFont('Calibri', 11))
        self.showSessionMessage(
            u'<font color=green>Correct: OK</font>\t|\tNext quiz: ' +
            self.srs.getNextQuizTime() + '\t|\t<font color=' +
            self.srs.getLeitnerGradeAndColor()['color'] + '>Grade: ' +
            self.srs.getLeitnerGradeAndColor()['grade'] + ' (' +
            self.srs.getLeitnerGradeAndColor()['name'] + ')<font>')

        #self.answered.setShortcut('5')
        #self.setFocus()

    def wrongAnswer(self):
        '''
        self.stats.musingsStopped()
        self.stats.postQuizStarted()
        
        self.stopCountdown()
        self.hideButtonsQuiz()
        
        self.getReadyPostLayout()
        '''
        self.postAnswerActions()

        self.srs.answeredWrong()
        self.stats.quizAnsweredWrong()

        #        self.answered.setText(self.srs.getCurrentSentenceTranslation())

        self.checkTranslationSize(self.srs.getCurrentSentenceTranslation())

        #self.answered.setFont(QFont('Calibri', 11))
        #self.showSessionMessage(u"Wrong! Should be: <font style='font-family:" + Fonts.MSMyoutyou + "'>"
        #+ self.srs.getCorrectAnswer() + "</font> - Next quiz: " + self.srs.getNextQuizTime())
        self.showSessionMessage(
            u'<font color=tomato>Bad</font>\t|\tNext quiz: ' +
            self.srs.getNextQuizTime() + '\t|\t<font color=' +
            self.srs.getLeitnerGradeAndColor()['color'] + '>Grade: ' +
            self.srs.getLeitnerGradeAndColor()['grade'] + ' (' +
            self.srs.getLeitnerGradeAndColor()['name'] + ')<font>')

    def timeIsOut(self):
        self.stats.musingsStopped()
        self.stats.postQuizStarted()

        QTimer.singleShot(
            50, self.hideButtonsQuiz
        )  #NB: slight artificial lag to prevent recursive repaint crush, when mouse is suddenly over appearing button
        self.getReadyPostLayout()

        self.srs.answeredWrong()
        self.stats.quizAnsweredWrong()

        #self.showSessionMessage(u'Time is out! Correct answer is:' + self.srs.getCorrectAnswer())
        #        self.answered.setFont(QFont('Calibri', 11))
        #        self.answered.setText(self.srs.getCurrentSentenceTranslation())

        self.checkTranslationSize(self.srs.getCurrentSentenceTranslation())

        self.showSessionMessage(
            u'<font color=tomato>Timeout!</font>\t|\tNext quiz: ' +
            self.srs.getNextQuizTime() + '\t|\t<font color=' +
            self.srs.getLeitnerGradeAndColor()['color'] + '>Grade: ' +
            self.srs.getLeitnerGradeAndColor()['grade'] + ' (' +
            self.srs.getLeitnerGradeAndColor()['name'] + ')<font>')

    def hideQuizAndWaitForNext(self):
        self.stats.postQuizEnded()

        self.status.hide()
        self.info.hide()
        self.allInfo.hide()
        self.resetButtonsActions()

        self.setWindowOpacity(1)
        self.fade()
        QTimer.singleShot(1000, self.hide)
        self.waitUntilNextTimeslot()
        #self.updater.mayUpdate = True

    def pauseQuiz(self):
        if self.isHidden():
            if self.pauseAction.text() == '&Pause':
                self.nextQuizTimer.stop()
                self.pauseAction.setText('&Unpause')
                self.pauseAction.setShortcut('U')
                self.trayIcon.setToolTip('Quiz paused!')

                self.trayIcon.setIcon(
                    QIcon(PATH_TO_RES + TRAY + 'inactive.png'))
                self.stats.pauseStarted()

                #self.updater.mayUpdate = True

            elif self.pauseAction.text() == '&Start quiz!':
                self.waitUntilNextTimeslot()
                self.pauseAction.setText('&Pause')
                self.pauseAction.setShortcut('P')
                self.trayIcon.setToolTip('Quiz in progress!')

                self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'active.png'))
            else:
                self.waitUntilNextTimeslot()
                self.pauseAction.setText('&Pause')
                self.pauseAction.setShortcut('P')
                self.trayIcon.setToolTip('Quiz in progress!')

                self.trayIcon.setIcon(QIcon(PATH_TO_RES + TRAY + 'active.png'))
                self.stats.pauseEnded()

                #self.updater.mayUpdate = False
        else:
            self.showSessionMessage(
                u'Sorry, cannot pause while quiz in progress!')

    def showQuiz(self):
        if self.isHidden():
            self.updateContent()
            self.setButtonsActions()

            self.show()
            self.setWindowOpacity(0)
            self.fade()

            self.countdown.setValue(self.options.getCountdownInterval() * 100)
            self.beginCountdown()
            self.stats.musingsStarted()

            if self.nextQuizTimer.isActive(): self.nextQuizTimer.stop()
            #self.updater.mayUpdate = False
        else:
            self.showSessionMessage(u'Quiz is already underway!')

    def showOptions(self):
        self.optionsDialog.show()

    def showAbout(self):
        self.about.show()

    def showQuickDict(self):
        self.qdict.showQDict = True

    def showGlobalStatistics(self):
        print '...'

    def startTrayLoading(self):
        self.gifLoading.start()
        #self.iconTimer = QTimer()
        #self.iconTimer.timeout.connect(self.updateTrayIcon)
        #self.iconTimer.start(100)

    def stopTrayLoading(self):
        self.gifLoading.stop()

    def updateTrayIcon(self):
        self.trayIcon.setIcon(self.gifLoading.currentPixmap())

    def showSessionMessage(self, message):
        """Shows info message"""
        self.status.message.setText(message)
        self.status.show()
        #self.setFocus() #NB: does not work

    def saveAndExit(self):
        self.hide()
        self.status.hide()
        self.allInfo.hide()
        self.trayIcon.showMessage('Shutting down...', 'Saving session',
                                  QSystemTrayIcon.MessageIcon.Information,
                                  20000)
        #self.startTrayLoading()

        if self.countdownTimer.isActive():
            self.countdownTimer.stop()
        if self.nextQuizTimer.isActive():
            self.nextQuizTimer.stop()
        if self.progressTimer != () and self.progressTimer.isAlive():
            self.progressTimer.cancel()

        self.srs.endCurrentSession()
        self.trayIcon.hide()

        #        self.hooker.stop()

        #self.updater.stop()
        self.optionsDialog.close()
        self.about.close()
        #self.qdict.close()
        self.close()

    def addReferences(self, about, options, qdict, updater):
        self.about = about
        self.optionsDialog = options
        self.qdict = qdict
        self.updater = updater

    def initGlobalHotkeys(self):
        def toggleWidgetFlag():
            self.qdict.showQDict = True

        self.hooker = GlobalHotkeyManager(toggleWidgetFlag, 'Q')
        self.hooker.setDaemon(True)
        self.hooker.start()
Beispiel #33
0
class Connector():
    '''Модуль для обеспечения взаимодействия модуля-обработчика OpenVPN 
    и интерфейса QML, обеспечивает полное многоуровневое логирование
    событий (через модуль logging) 
    '''
    def __init__(self):
        '''Инициализация модуля
        
        Выполняется инициализация интерфейса, логгера и статуса соединения
        Определяется операционная система и задаются соответствующие пути 
        '''
        self.view = Interface(self)
        self.logger = logging.getLogger('RocketOne.Connector')
        self.connected = False
        # Пути до OpenVPN
        if os.name == "nt":
            #Windows paths
            self.ovpnpath = 'C:\\Program Files (x86)\\OpenVPN'
            self.path = getBasePath() + '/'
            self.ovpnconfigpath = self.ovpnpath + '\\config\\'
            self.configfile = 'config.ini'  # self.ovpnconfigpath +
            self.ovpnexe = self.ovpnpath + '\\bin\\openvpnserv.exe'
            self.traymsg = 'OpenVPN Connection Manager'
            self.logger.debug("Started on Windows")
        elif os.name == "posix":
            #Linux Paths
            self.ovpnpath = ''
            self.path = getBasePath() + '/'
            self.ovpnconfigpath = self.ovpnpath + '//home//alexei//SOLOWAY//'
            self.ovpnexe = self.ovpnpath + 'openvpn'
            self.configfile = 'config.ini'
            self.traymsg = 'OpenVPN Connection Manager'
            self.logger.debug("Started on Linux")

    # Интерфейс обрабатывающий входящие сигналы
    @QtCore.Slot(str, str)
    def connect(self, login, passwd):
        """ Процедура инициализации соединения
        Является слотом QT (для получения логина и пароля из QML)
        """
        self.port = 0
        port = self.getNextAvailablePort()
        # Если логин и пароль пусты или дефолтные то не соединяемся
        if (not login) or (not passwd) \
            or (login == "Login") or (passwd == "password"):
            return
        self.login = login
        self.password = passwd
        # если стоит галочка запомнить пароль TODO: убрать конструкцию
        if self.view.remember():
            self.write_settings(login, passwd, remember=True)
        else:
            self.write_settings("", "")
        # Пробуем запустить OpenVPN, не получается - не подключаемся
        try:
            self.process = subprocess.Popen([
                self.ovpnexe, '--config', self.ovpnconfigpath + 'Soloway.ovpn',
                '--management', '127.0.0.1', '{0}'.format(port),
                '--management-query-passwords', '--management-log-cache',
                '200', '--management-hold'
            ],
                                            cwd=self.ovpnconfigpath)
        except Exception as e:
            self.logger.error("OpenVPN process failed to execute " + str(e))
            return
        self.logger.debug("Subprocess started")
        # Задаем таймер для asyncore loop устанавливаем на 1 раз в секунду
        self.timer = QTimer()
        self.timer.connect(SIGNAL("timeout()"), self.looper)
        self.timer.start(500)
        self.port = port
        """ Устанавливаем однократный таймер для запуска обработчика OpenVPN
        Без таймера openvpn не успевает запуститься, так что при
        попытке соединения с ним asyncore.async_chat выпадает 
        """
        self.atimer = QTimer()
        self.atimer.setSingleShot(True)
        self.atimer.timeout.connect(self.manage_process)
        self.atimer.start(1000)
        # Записываем время старта, через 20 секунд без подключения - disconnect
        self.start_time = datetime.now()
        # Отправляем сигнал в QML о начале соединения
        self.emit_signal("100")

    def looper(self):
        """ looper проверяет не наступил ли таймаут подключения и
        собирает данные с async_chat. Завязан на таймер self.timer
        """
        ex_time = datetime.now() - self.start_time
        ex_time = ex_time.total_seconds()
        if (ex_time > 20) and not self.connected:
            self.logger.error("Connection time out")
            self.disconnect("401")
        asyncore.poll()

    def manage_process(self):
        """Запускает обработчик OpenVPN. Выделить в отдельную процедуру пришлось,
        так как завязан на однократный таймер self.atimer.
        """
        self.sock = ManagementInterfaceHandler(self, '127.0.0.1', self.port)

    def read_settings(self):
        """Чтение конфига программы через ConfigParser
        Если что-то находит - записывает во внутренние переменные
        """
        self.config = ConfigParser()
        self.config.read(self.configfile)
        if not self.config.has_section('Auth'):
            return
        login = self.config.get('Auth', 'User')
        password = self.config.get('Auth', 'Password')
        if self.config.has_section('Options'):
            remember = self.config.get('Options', 'Remember')
            self.view.set_remember(remember)
        else:
            self.view.set_remember(False)
        if login and password:
            self.view.set_auth(login, password)
            self.login = login
            self.password = password

    def write_settings(self, login, passwd, remember=False):
        """Запись конфига программы"""
        self.config = ConfigParser()
        self.config.add_section("Auth")
        self.config.set("Auth", "User", login)
        self.config.set("Auth", "Password", passwd)
        self.config.add_section("Options")
        self.config.set("Options", "Remember", remember)
        with open(self.configfile, 'wb') as configfile:
            self.config.write(configfile)

    @QtCore.Slot(str)
    def disconnect(self, status="400"):
        """Прекращение соединения и придание забвению всех процессов
        и обработчиков (если они есть конечно)
        """
        self.logger.debug("Shutting down connection")
        self.port = 0
        self.emit_signal(status)
        if hasattr(self, "timer"):
            if self.timer:
                self.timer.stop()
                self.timer = None
        if hasattr(self, "sock"):
            if self.sock:
                self.sock.send('signal SIGTERM\n')
                self.sock = None
        # уничтожает процесс если он еще не уничтожен
        if hasattr(self, "process") and self.process:
            try:
                self.process.terminate()
            except WindowsError:
                self.logger.info("Process has been destroyed correctly")
            self.process = None

    def emit_signal(self, status):
        """Отправка сигнала в дальний космос (интерфейс QML)
        """
        self.logger.debug("Emit signal " + status)
        self.view.emit_signal(status)
        if status == "200":
            """Значит подключение было совершено и
            можно сделать запись в журнале и спрятать
            окошко в трей. И пусть весь мир подождет....
            """
            self.logger.debug("Connection initiated")
            self.connected = True

    def got_log_line(self, line):
        """Called from ManagementInterfaceHandler when new log line is received."""
        pass

    def got_state_line(self, line):
        """Called from ManagementInterfaceHandler when new line describing current OpenVPN's state is received."""
        list = line.split(',', 2)
        state = list[1]
        if state == 'CONNECTED':
            self.emit_signal("200")
        elif state == 'TCP_CONNECT':
            self.emit_signal("102")
        elif state == 'AUTH':
            self.emit_signal("103")
        elif state == 'GET_CONFIG':
            self.emit_signal("104")
        elif state == 'ASSIGN_IP':
            self.emit_signal("105")

    #Мясцо
    def getNextAvailablePort(self):
        """Returns next minimal unused port starting from 10598."""
        minport = 10598
        found = False
        while not found:
            found = True
            if self.port != 0:
                if self.port == minport:
                    found = False
                    minport += 1
                    break
        return minport
Beispiel #34
0
class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setupUi(self)
        self.btnScrape.clicked.connect(self.doScrape)

        self.q = Queue()
        self.timer = QTimer()
        self.timer.timeout.connect(self.updateProgress)

    def doScrape(self):
        if not self.validateInput():
            return
        Con = self.buildInput()
        QMessageBox.information(self, 'Starting...',
                                'Starting Scrape, Please be patient !!!')
        self.timer.start(100)
        p = Process(target=doScrape, args=(Con, self.q))
        p.daemon = True
        p.start()

    def updateProgress(self):
        if self.q.empty():
            return
        q_msg, q_progress = self.q.get()
        self.lblProgress.setText(q_msg)
        self.progressBar.setValue(q_progress)

        if q_progress == 100:
            self.timer.stop()
            if q_msg == 'SUCCESS':
                QMessageBox.information(self, 'Done', 'Scrape Successful !!!')
            else:
                QMessageBox.critical(self, 'Error Occurred',
                                     'Error Occurred in Scraping')
        self.updateProgress()

    def validateInput(self):
        if self.txtUsername.text().strip() == '':
            QMessageBox.critical(self, 'Validation Error',
                                 'Please enter User Name')
            self.txtUsername.setFocus()
            return False
        elif self.txtPassword.text().strip() == '':
            QMessageBox.critical(self, 'Validation Error',
                                 'Please enter Password')
            self.txtPassword.setFocus()
            return False
        elif self.txtFromDate.date() > self.txtToDate.date():
            QMessageBox.critical(
                self, 'Validation Error',
                'From Date sould not be greater than To Date')
            self.txtFromDate.setFocus()
            return False
        else:
            return True

    def buildInput(self):
        Con = Config()
        Con.Username = self.txtUsername.text().strip()
        Con.Password = self.txtPassword.text().strip()
        d = self.txtFromDate.date()
        Con.FromDate = date(d.year(), d.month(), d.day())
        d = self.txtToDate.date()
        Con.ToDate = date(d.year(), d.month(), d.day())
        Con.Label = str(self.txtLabel.text().strip())
        if Con.Label == '':
            Con.Label = 'Inbox'
        return Con
Beispiel #35
0
class VideoMediaView(MediaView):
    def __init__(self, media, parent):
        super(VideoMediaView, self).__init__(media, parent)
        self._widget = QWidget(parent)
        self._process = QProcess(self._widget)
        self._process.setObjectName('%s-process' % self.objectName())
        self._std_out = []
        self._errors = []
        self._stopping = False
        self._mute = False
        if 'mute' in self._options:
            self._mute = bool(int(self._options['mute']))

        self._widget.setGeometry(media['_geometry'])
        self.connect(self._process, SIGNAL("error()"), self._process_error)
        self.connect(self._process, SIGNAL("finished()"), self.stop)
        self.connect(self._process, SIGNAL("readyReadStandardOutput()"),
                     self.__grep_std_out)
        self.set_default_widget_prop()

        self._stop_timer = QTimer(self)
        self._stop_timer.setSingleShot(True)
        self._stop_timer.setInterval(1000)
        self._stop_timer.timeout.connect(self._force_stop)

    @Slot()
    def _force_stop(self):
        os.kill(self._process.pid(), signal.SIGTERM)
        self._stopping = False
        if not self.is_started():
            self.started_signal.emit()
        super(VideoMediaView, self).stop()

    @Slot(object)
    def _process_error(self, err):
        self._errors.append(err)
        self.stop()

    @Slot()
    def play(self):
        self._finished = 0
        path = "%s/%s" % (self._save_dir, self._options['uri'])
        self._widget.show()
        args = [
            '-slave', '-identify', '-input',
            'nodefault-bindings:conf=/dev/null', '-wid',
            str(int(self._widget.winId())), path
        ]
        if self._mute:
            args += ['-ao', 'null']

        self._process.start('mplayer', args)
        self._stop_timer.start()

    @Slot()
    def stop(self, delete_widget=False):
        if self._stopping or self.is_finished():
            return False
        self._stop_timer.start()
        self._stopping = True
        if self._process.state() == QProcess.ProcessState.Running:
            self._process.write("quit\n")
            self._process.waitForFinished(50)
            self._process.close()

        super(VideoMediaView, self).stop(delete_widget)
        self._stopping = False
        self._stop_timer.stop()
        return True

    @Slot()
    def __grep_std_out(self):
        lines = self._process.readAllStandardOutput().split("\n")
        for line in lines:
            if not line.isEmpty():
                if line.startsWith("Starting playback"):
                    self._widget.raise_()
                    self._play_timer.start()
                    self.started_signal.emit()
                    self._stop_timer.stop()
                else:
                    part = line.split("=")
                    if 'ID_LENGTH' == part[0]:
                        if float(
                                self._duration
                        ) > 0:  # user set the video duration manually.
                            self._play_timer.setInterval(
                                int(1000 * float(self._duration)))
                        else:  # use duration found by mplayer.
                            self._play_timer.setInterval(
                                int(1000 * float(part[1])))
Beispiel #36
0
class QtReactor(posixbase.PosixReactorBase):
    implements(IReactorFDSet)

    def __init__(self):
        self._reads = {}
        self._writes = {}
        self._notifiers = {}
        self._timer = QTimer()
        self._timer.setSingleShot(True)
        QObject.connect(self._timer, SIGNAL("timeout()"), self.iterate)

        if QCoreApplication.instance() is None:
            # Application Object has not been started yet
            self.qApp = QCoreApplication([])
            self._ownApp = True
        else:
            self.qApp = QCoreApplication.instance()
            self._ownApp = False
        self._blockApp = None
        posixbase.PosixReactorBase.__init__(self)

    def _add(self, xer, primary, type):
        """
Private method for adding a descriptor from the event loop.

It takes care of adding it if new or modifying it if already added
for another state (read -> read/write for example).
"""
        if xer not in primary:
            primary[xer] = TwistedSocketNotifier(None, self, xer, type)

    def addReader(self, reader):
        """
Add a FileDescriptor for notification of data available to read.
"""
        self._add(reader, self._reads, QSocketNotifier.Read)

    def addWriter(self, writer):
        """
Add a FileDescriptor for notification of data available to write.
"""
        self._add(writer, self._writes, QSocketNotifier.Write)

    def _remove(self, xer, primary):
        """
Private method for removing a descriptor from the event loop.

It does the inverse job of _add, and also add a check in case of the fd
has gone away.
"""
        if xer in primary:
            notifier = primary.pop(xer)
            notifier.shutdown()

    def removeReader(self, reader):
        """
Remove a Selectable for notification of data available to read.
"""
        self._remove(reader, self._reads)

    def removeWriter(self, writer):
        """
Remove a Selectable for notification of data available to write.
"""
        self._remove(writer, self._writes)

    def removeAll(self):
        """
Remove all selectables, and return a list of them.
"""
        rv = self._removeAll(self._reads, self._writes)
        return rv

    def getReaders(self):
        return self._reads.keys()

    def getWriters(self):
        return self._writes.keys()

    def callLater(self, howlong, *args, **kargs):
        rval = super(QtReactor, self).callLater(howlong, *args, **kargs)
        self.reactorInvocation()
        return rval

    def reactorInvocation(self):
        self._timer.stop()
        self._timer.setInterval(0)
        self._timer.start()

    def _iterate(self, delay=None, fromqt=False):
        """See twisted.internet.interfaces.IReactorCore.iterate.
"""
        self.runUntilCurrent()
        self.doIteration(delay, fromqt)

    iterate = _iterate

    def doIteration(self, delay=None, fromqt=False):
        'This method is called by a Qt timer or by network activity on a file descriptor'

        if not self.running and self._blockApp:
            self._blockApp.quit()
        self._timer.stop()
        delay = max(delay, 1)
        if not fromqt:
            self.qApp.processEvents(QEventLoop.AllEvents, delay * 1000)
        if self.timeout() is None:
            timeout = 0.1
        elif self.timeout() == 0:
            timeout = 0
        else:
            timeout = self.timeout()
        self._timer.setInterval(timeout * 1000)
        self._timer.start()

    def runReturn(self, installSignalHandlers=True):
        self.startRunning(installSignalHandlers=installSignalHandlers)
        self.reactorInvocation()

    def run(self, installSignalHandlers=True):
        if self._ownApp:
            self._blockApp = self.qApp
        else:
            self._blockApp = QEventLoop()
        self.runReturn()
        self._blockApp.exec_()
Beispiel #37
0
class Sync(QObject):
    
    deleteServerFile = Signal((str,))
    deleteLocalFile = Signal((str,))
    downloadFile = Signal((str,))
    uploadFile = Signal((str,))
    checkServer = Signal()
    checkLocal = Signal()
    statusChanged = Signal((str,))

    def __init__(self, host, ssl, parent=None):
        super(Sync, self).__init__(parent)

        self.server = ServerWatcher(host, ssl, self)
        
        self.preloaedActions = []
        self.doPreemptive = empty_db()
        self.connected = False
        self.firstScan = True
        
    def setLocalDir(self, localdir):
        if not os.path.exists(localdir):
            # Creates the directory if it doesn't already exists.
            os.makedirs(localdir)
        self.local = LocalWatcher(localdir)
        self.server.setLocalDir(localdir)

        self.local.moveToThread(self.thread())
        self.local.setParent(self)
    
    def connections(self):
        if not self.connected:
            self.connected = True
            self.server.fileAdded.connect(self.onAdded)
            self.server.fileChanged.connect(self.onChanged)
            self.server.fileDeleted.connect(self.onDeleted)

            self.local.fileAdded.connect(self.onAdded)
            self.local.fileChanged.connect(self.onChanged)
            self.local.fileDeleted.connect(self.onDeleted)

            self.deleteLocalFile.connect(self.local.deleteFile)
            self.deleteServerFile.connect(self.server.onDelete)
            self.downloadFile.connect(self.server.onDownload)
            self.uploadFile.connect(self.server.onUpload)

    @Slot()
    def initQueue(self):
        self.actionQueue = ActionQueue()
        
        self.actionTimer = QTimer()
        self.actionTimer.setInterval(1)
        self.actionTimer.timeout.connect(self.takeAction)
        
        self.actionTimer.start()
    
    @Slot()
    def takeAction(self):
        self.actionTimer.stop()

        if self.doPreemptive:
            # Preemptive check is a bit of a workaround to deal with
            # initial unexpected conditions: database file is gone
            self.doPreemptive = False
            self.server.preemptiveCheck = True
            self.local.fileAdded.connect(self.server.added)
            self.local.checkout()
            self.server.checkout()
            self.local.fileAdded.disconnect(self.server.added)
            self.server.preemptiveCheck = False
            for action in self.server.preemptiveActions:
                self.actionQueue.add(action)
        
        # After preemptive check, it is safe to do the connections
        # for normal operations
        self.connections()

        serverActionCount = 0
        localActionCount = 0
        for action in self.actionQueue:
            if action is not None:
                print 'Next action: %s' % action 
                path = action.path
                do = action.action
                location = action.location
                
                if location == FileAction.LOCAL and (do == FileAction.UPLOAD \
                   or do == FileAction.DELETE):
                    if not engine_tools.file_exists_local(path):
                        # File no longer exists at the time of processing.
                        # Maybe it was temporary or a quick rename.
                        # So we ignore it
                        print "Ignored action on " + path + ": File doesn't exist on local."
                        continue
                    
                
                if do == FileAction.UPLOAD:
                    self.uploadFile.emit(path)
                    localActionCount += 1
                elif do == FileAction.DOWNLOAD:
                    self.downloadFile.emit(path)
                    serverActionCount += 1
                elif do == FileAction.DELETE:
                    with File.fromPath(path) as deleted_file:
                        # `action.location` attribute only makes sense when deciding
                        # whether to delete a file on the server or local.
                        if location == FileAction.LOCAL:
                            localpath = self.local.localFromServer(path)
                            self.deleteLocalFile.emit(localpath)
                            deleted_file.inlocal = False
                            localActionCount += 1

                        elif location == FileAction.SERVER:
                            self.deleteServerFile.emit(path)
                            deleted_file.inserver = False
                            serverActionCount += 1
        
        self.actionQueue.clear()
        
        # Scan server for file changes
        self.statusChanged.emit('Scanning remote files for changes')
        self.server.checkout()
        
        if self.firstScan:
            # First do a full scan to check for offline changes.
            # From there we will rely on real time notifications watchdog.
            self.firstScan = False
            self.statusChanged.emit('Scanning local files for changes')
            self.local.checkout()
            self.local.startObserver()
            # Si Added
            # Since its the first scan, we should also
            # set the timer interval
            self.actionTimer.setInterval(5000)        
        self.cleanSync()

        # Si Added
        # Set check interval intelligently.
        # If there's no activity there, wait longer.
        # Since if there's just no usage, then
        # no reason to take up CPU cycles.
        tempInterval = 0
        if serverActionCount+localActionCount > 0:
            tempInterval = 5000
        else:
            tempInterval = 1000 * 10
        
        self.actionTimer.start()
            
    @Slot()
    def cleanSync(self):
        """
        Removes entries from the database for deleted files
        """
        
        session = Session()
        session.query(File).filter(File.inserver == False).filter(File.inlocal == False).delete(synchronize_session=False)
        session.commit()
        self.statusChanged.emit('Sync completed. Waiting for changes')

    @Slot(str, str, bool)
    def onChanged(self, location, serverpath, skipDeltaCheck):
        changed_file = File.fromPath(serverpath)
        action = None
        
        #if not changed_file.servermdate:
            # Probably a local added event that also
            # spawned a modified event.
            #return

        file_name_only = os.path.basename(serverpath)
        if engine_tools.isTemporaryFile(file_name_only):
            print 'File ' + serverpath + ' ignored since it is a temporary file'
            return
           
        print 'File ' + serverpath + ':'
        
        if changed_file.servermdate == None:
            mydiff = "** File Not in Server **"
            edit_time = "(not in server)"
        else:
            ttt = (changed_file.localmdate - changed_file.servermdate).total_seconds() 
            mydiff = str( ttt )
            edit_time = str(changed_file.servermdate)
        
        print 'Changed here %s, there %s delta %s' % (
                    changed_file.localmdate, edit_time, mydiff)
                    
        
        try:
            if changed_file.inserver:
                diff = changed_file.timeDiff()

                MY_TOLERANCE = 10
            
                if skipDeltaCheck == False and abs(diff) < MY_TOLERANCE:
                    return
            
            if location == FileAction.SERVER:
                if changed_file.inlocal:
                    if changed_file.localmdate < changed_file.servermdate:
                        action = FileAction(serverpath, FileAction.DOWNLOAD, FileAction.LOCAL)
                else:
                    action = FileAction(serverpath, FileAction.DOWNLOAD, FileAction.LOCAL)
           
            elif location == FileAction.LOCAL:
                if changed_file.inserver:
                    try:
                        if changed_file.servermdate < changed_file.localmdate:
                            action = FileAction(serverpath, FileAction.UPLOAD, FileAction.SERVER)
                    except:
                        print 'Error:', changed_file, changed_file.servermdate, changed_file.localmdate
                        
                else:
                    action = FileAction(serverpath, FileAction.UPLOAD, FileAction.SERVER)
                
            if action is not None:
                self.actionQueue.add(action)
        except:
            info = traceback.format_exception(*sys.exc_info())
            for i in info: sys.stderr.write(i)
            
    
    @Slot(str, str)
    def onAdded(self, location, serverpath):

        file_name_only = os.path.basename(serverpath)
        if engine_tools.isTemporaryFile(file_name_only):
            print 'File ' + serverpath + ' was created but ignored since it is a temporary file'
            return
        
        added_file = File.fromPath(serverpath)
        action = None
        
        if location == FileAction.SERVER and not added_file.inlocal:
            action = FileAction(serverpath, FileAction.DOWNLOAD, FileAction.LOCAL)
        elif location == FileAction.LOCAL and not added_file.inserver:
            action = FileAction(serverpath, FileAction.UPLOAD, FileAction.SERVER)
            
        if action is not None:
            self.actionQueue.add(action)
        
    @Slot(str, str)
    def onDeleted(self, location, serverpath):

        # NOTE: For temporary files, the current action is to delete it.
        # Reason 1: We need to remove it from the database.
        # Reason 2: If somehow there is a temporary file 
        # there on the other side, then it makes sense to delete it.
        
        deleted_file = File.fromPath(serverpath)
        action = None
        
        if location == FileAction.SERVER:
            if deleted_file.inlocal:
                action = FileAction(serverpath, FileAction.DELETE, FileAction.LOCAL)
        elif location == FileAction.LOCAL:
            if deleted_file.inserver:
                action = FileAction(serverpath, FileAction.DELETE, FileAction.SERVER)
        
        if action is not None:
            self.actionQueue.add(action)
class ClimateControlModel(QObject):
    """ Model class representing the climate control system

    Talks with flow, fetches required data from flow, caches some of the data
    """
    __message_received_signal = None
    setting_received = Signal(dict)
    save_setting_result = Signal(dict)
    measurement_changed = Signal(MeasurementEvent)
    relay_status_changed = Signal(RelayStatusEvent)
    device_status_changed = Signal(DeviceStatusEvent)
    controller_status = Signal(str)
    command_response_received = Signal(dict)
    command_sending_result = Signal(dict)
    connection_status = Signal(dict)
    latency_changed = Signal(float)

    def __init__(self):
        """ ClimateControlModel constructor

        Initializes Flow library, thread pool
        """
        super(ClimateControlModel, self).__init__()
        self.__thread_pool = QThreadPool()
        self.__thread_pool.setMaxThreadCount(THREAD_POOL_MAX_THREADS)
        self.__flow_user = None
        self.__controller_device = None
        self.__checking_connection_status = False
        self.__last_measurement_event = None
        self.__last_relay_status_event = None
        self.__last_device_status_event = None
        self.__controller_heartbeat = None
        self.latency = Latency()
        self.__latency_timer = QTimer(self)
        self.__latency_timer.timeout.connect(self.__latency_timer_expired)
        ClimateControlModel.__message_received_signal = MessageReceived()
        ClimateControlModel.__message_received_signal.signal.connect(self.__message_received)

    def initialize(self, user_name, password):
        """ Initializes the system

        Initializes flow libraries and connects to server
        Logs in as user and tries to find the controller device, caches if found
        :param str user_name: flow user name
        :param str password: flow user password
        :raises ControllerNotFound, LoginFailed, FlowLibraryError
        """
        flow_url, flow_key, flow_secret = get_server_details()
        try:
            flow_initialize(flow_url, flow_key, flow_secret)
        except FlowException as flow_error:
            if flow_error.connection_error:
                self.check_connection_status()
            LOGGER.exception(flow_error)
            raise FlowLibraryError("Failed to initialize flow library")

        try:
            self.__flow_user = FlowUser(user_name, password)
        except FlowException as flow_error:
            if flow_error.connection_error:
                self.check_connection_status()
            LOGGER.exception(flow_error)
            raise LoginFailed()
        except ValueError as value_error:
            LOGGER.exception(value_error)
            raise LoginFailed()

        try:
            self.__controller_device = self.__flow_user.find_device(CONTROLLER_DEVICE_TYPE)
        except FlowException as flow_error:
            if flow_error.connection_error:
                self.check_connection_status()
            LOGGER.exception(flow_error)
            raise ControllerNotFound()

        # enable message reception
        self.__flow_user.enable_message_reception(ClimateControlModel.message_received_callback)

    def close(self):
        """ De-initialize the model

        This function won't return till all the worker thread finish their job
        """
        LOGGER.debug("waiting for threads in thread pool")
        self.__thread_pool.waitForDone()
        LOGGER.debug("thread pool exit done")
        if self.__flow_user:
            self.__flow_user.logout()

    def get_settings(self):
        """ Get setting for the controller device

        Schedules a worker thread for getting setting
        """
        runnable = WorkerThread(get_setting_work, self.__settings_received,
                                device=self.__controller_device,
                                key=ControllerSetting.controller_config_key)
        self.__thread_pool.start(runnable)

    def save_settings(self, setting):
        """ Save setting of controller device

        :param ControllerSetting setting: setting object to be saved in KVS
        """
        setting_xml = setting.to_xml()
        LOGGER.debug("Trying to save setting xml = {}".format(setting_xml))
        runnable = WorkerThread(save_setting_work, self.__save_settings_result,
                                device=self.__controller_device,
                                key=ControllerSetting.controller_config_key,
                                value=setting_xml)
        self.__thread_pool.start(runnable)

    def send_command(self, command):
        """ Send command to the controller device

        :param ControllerCommandEnum command: command to be sent
        """
        if command not in ControllerCommandEnum:
            raise InvalidCommand("Command not supported")

        LOGGER.info("sending command {}".format(command.value))
        command_xml = ControllerCommand(command).xml
        LOGGER.debug("command xml {}".format(command_xml))
        runnable = WorkerThread(send_command_work, self.__command_sent_result,
                                user=self.__flow_user, device=self.__controller_device,
                                message=command_xml)
        self.__thread_pool.start(runnable)

    def check_connection_status(self):
        """ Starts worker thread for checking connection status
        """
        if not self.__checking_connection_status:
            self.__checking_connection_status = True
            runnable = WorkerThread(check_connection_status_work, self.__connection_status_result)
            self.__thread_pool.start(runnable)

    @Slot(str, str)
    def __settings_received(self, setting, error):
        """ Slot function called when get setting worker thread finishes the task

        :param str setting: received setting from flow
        :param str error: error string if any
        """
        parsed_setting = None
        if not error:
            LOGGER.debug("Received setting xml = {}".format(setting))
            try:
                parsed_setting = ControllerSetting(setting_xml=setting)
                if not self.__controller_heartbeat:
                    # start heartbeat timer with twice the heartbeat period so that even if some
                    # heartbeat message failed to receive, it should wait for another
                    # heartbeat message
                    self.__controller_heartbeat = \
                        HeartBeatTimer(parsed_setting.controller_heartbeat*2*1000,
                                       self.__heartbeat_timer_status_changed)
                else:
                    # if heartbeat already exist then restart with new time
                    self.__controller_heartbeat.change_timeout_period(
                        parsed_setting.controller_heartbeat*2*1000)
            except ValueError as value_error:
                LOGGER.exception(value_error)
                error = "Invalid setting XML received"
        else:
            if error["connection_error"]:
                self.check_connection_status()

        self.setting_received.emit({"setting": parsed_setting, "error": error})

    @Slot(str)
    def __save_settings_result(self, error=None):
        """ Slot function for the save setting worker function result

        :param str error: error string if any
        """
        self.save_setting_result.emit({"error": error})
        if error:
            LOGGER.debug("Save setting failed: {}".format(error))
            if error["connection_error"]:
                self.check_connection_status()
        else:
            # if KVS updated then send message to device to update the settings
            self.send_command(ControllerCommandEnum.retrieve_settings)
            LOGGER.debug("Setting saved to KVS")

    @Slot(bool, bool)
    def __connection_status_result(self, network, internet):
        """ Slot function for connection status result
        :param bool network: False if network is down
        :param bool internet: False if internet is down
        """
        self.__checking_connection_status = False
        connection_status = {"network": network, "internet": internet}
        self.connection_status.emit(connection_status)

    @Slot(str)
    def __command_sent_result(self, error=None):
        """ Slot function called at the end of sending command

        :param str error: error string if any
        """
        if error:
            self.command_sending_result.emit({"error": error})
            LOGGER.debug("Message sending failed: {}".format(error))
            if error["connection_error"]:
                self.check_connection_status()
        else:
            LOGGER.debug("Message sending success")

# pylint: disable=invalid-name
    @Slot(str)
    def __heartbeat_timer_status_changed(self, status):
        """ Slot function called when heartbeat timer changes its status(start, expire etc)

        It receives controller status. on timer expire-controller status = "OFFLINE"
        on timer refresh(start)- controller status = "ONLINE"
        :param str status: "ONLINE" or "OFFLINE" status
        """
        self.controller_status.emit(status)
        if status == "OFFLINE":
            # timer expired so there might be something wrong with the
            # network or internet so check connection status
            self.check_connection_status()
            LOGGER.debug("Latency timer stopped")
            self.__latency_timer.stop()
            if self.__last_device_status_event:
                # if controller is OFFLINE, make sensor and actuator also OFFLINE
                self.__last_device_status_event.sensor_alive = False
                self.__last_device_status_event.actuator_alive = False
                self.device_status_changed.emit(self.__last_device_status_event)
        else:
            self.__latency_timer.start(LATENCY_PERIOD*1000)
            LOGGER.debug("Latency timer started")
# pylint: enable=invalid-name

    @Slot()
    def __latency_timer_expired(self):
        """ Slot called on the expiry of latency timer

        """
        # send ping message to controller and calculate latency based on when it is received
        self.send_command(ControllerCommandEnum.ping)

    def __handle_command_response(self, response_dict):
        """ Parse the received response and emit signal if valid response found

        :param dict response_dict: response xml dictionary
        """
        try:
            response = ControllerResponse(response_dict)
            LOGGER.info("received response {}".format(response.response.value))
            if response.response == ControllerResponseEnum.ping:
                # round_trip_time is the difference between current local time and timestamp
                # when message was sent, this includes any processing delay on controller side
                round_trip_time = (datetime.datetime.utcnow() - response.sent_time).total_seconds()
                LOGGER.debug("round_trip_time: {}".format(round_trip_time))
                # Ignore value where round_trip_time > MAX_LATENCY_TIME
                if round_trip_time <= MAX_LATENCY_VALUE:
                    latency = self.latency.calculate_exponential_moving_average(round_trip_time)
                else:
                    latency = round_trip_time
                self.latency_changed.emit(latency)
                LOGGER.debug("Latency: {}".format(latency))
            else:
                self.command_response_received.emit({"response": response.response})

            # if retrieve_settings_success is received, get the settings again as it might
            # be update by some other app
            # for e.g. admin app updating threshold and display app updating its values accordingly
            if response.response == ControllerResponseEnum.retrieve_settings_success:
                self.get_settings()
        except ValueError as error:
            LOGGER.exception(error)

    def __process_heartbeat_event(self, event):
        """ Processes HeartBeat event data

        :param HeartBeatEvent event: HeartBeat event object
        """
        if self.__last_measurement_event:
            if not event.measurement_data == self.__last_measurement_event:
                self.measurement_changed.emit(event.measurement_data)
                self.__last_measurement_event = event.measurement_data
            else:
                LOGGER.debug("Ignoring measurement data")
        else:
            self.measurement_changed.emit(event.measurement_data)
            self.__last_measurement_event = event.measurement_data

        LOGGER.debug("Heartbeat Temp: {} Humidity: {} ".format(event.measurement_data.temperature,
                                                               event.measurement_data.humidity))

        if self.__last_relay_status_event:
            if not event.relay_status == self.__last_relay_status_event:
                self.relay_status_changed.emit(event.relay_status)
                self.__last_relay_status_event = event.relay_status
            else:
                LOGGER.debug("Ignoring relay status data")
        else:
            self.relay_status_changed.emit(event.relay_status)
            self.__last_relay_status_event = event.relay_status

        if self.__last_device_status_event:
            if not event.device_status == self.__last_device_status_event:
                self.device_status_changed.emit(event.device_status)
                self.__last_device_status_event = event.device_status
            else:
                LOGGER.debug("Ignoring device status data")
        else:
            self.device_status_changed.emit(event.device_status)
            self.__last_device_status_event = event.device_status

        LOGGER.debug("Heartbeat Temp: {} Humidity: {} "
                     "Relay 1 ON: {} Relay 2 ON: {} "
                     "Sensor: {} Actuator: {}".format(event.measurement_data.temperature,
                                                      event.measurement_data.humidity,
                                                      event.relay_status.relay_1_on,
                                                      event.relay_status.relay_2_on,
                                                      event.device_status.sensor_alive,
                                                      event.device_status.actuator_alive))

    def __handle_event(self, message_dict):
        """ Create event object according to event type and handle the event

        :param dict message_dict: message content in the dictionary format
        """
        try:
            event = ControllerEventFactory.create_event(message_dict)
            if self.__controller_heartbeat:
                self.__controller_heartbeat.refresh_timer()

            if isinstance(event, MeasurementEvent):
                self.measurement_changed.emit(event)
                self.__last_measurement_event = event
                LOGGER.debug("Measurement Temp: {} Humidity: {}".format(event.temperature,
                                                                        event.humidity))
            elif isinstance(event, RelayStatusEvent):
                self.relay_status_changed.emit(event)
                self.__last_relay_status_event = event
                LOGGER.debug("RelayStatus Relay 1 ON: {} Relay 2 ON: {}".format(event.relay_1_on,
                                                                                event.relay_2_on))
            elif isinstance(event, DeviceStatusEvent):
                self.device_status_changed.emit(event)
                self.__last_device_status_event = event
                LOGGER.debug("DeviceStatus Sensor: {} Actuator: {}".format(event.sensor_alive,
                                                                           event.actuator_alive))
            elif isinstance(event, HeartBeatEvent):
                self.__process_heartbeat_event(event)

        except ValueError as value_error:
            LOGGER.exception(value_error)

    @Slot(str)
    def __message_received(self, message_content):
        """ Slot function called when message is received by callback function

        This function parses message_content(xml_string) into dictionary
        :param str message_content: message content which is xml string
        """
        LOGGER.debug("Received message {}".format(message_content))
        # We have received a message from controller means network and internet are up
        status = {"network": True, "internet": True}
        self.connection_status.emit(status)
        try:
            message_dict = xmltodict.parse(message_content)
            root_tag = message_dict.keys()[0]
            if root_tag == "response":
                self.__handle_command_response(message_dict)
            elif root_tag == "event":
                self.__handle_event(message_dict)
            else:
                LOGGER.error("Unsupported message received {}".format(message_content))
        except (ExpatError, KeyError) as error:
            LOGGER.exception("Error in parsing xml {} xml={}".
                             format(error.message, message_content))

    @staticmethod
    def message_received_callback(flow_message):
        """ Callback called by library, libflow expects this to be a static class method

        :param flow_message: message object from library
        """
        message = FlowMessage(flow_message)
        message_content = message.get_message_content()
        ClimateControlModel.__message_received_signal.signal.emit(message_content)
Beispiel #39
0
class Fight(QObject):
    def __init__(self, mw):
        super().__init__()

        self._mw = mw

        # Кнопка "Отнять у слабого"
        self._css_path_button_fight = "div[class='button-big btn f1']"

        # Кнопка поедания Сникерса
        self._css_path_button_snikers = 'div[onclick*=snikers]'

        # Кнопка использования Тонуса
        self._css_path_button_use_tonus = 'div[onclick*=tonus]'

        # Таймер для ожидания загрузки страницы с выбором противника
        self._timer_enemy_load = QTimer()
        self._timer_enemy_load.setInterval(333)
        self._timer_enemy_load.timeout.connect(self._check_enemy_load)

        # Таймер для поиска противника
        self._timer_next_enemy = QTimer()
        self._timer_next_enemy.setInterval(1000)
        self._timer_next_enemy.setSingleShot(True)
        self._timer_next_enemy.timeout.connect(self._next_enemy)

        # Информация о противнике: имя, уровень, url
        self.enemy_name = None
        self.enemy_level = None
        self.enemy_url = None

        # Выигрыш / проигрыш. Если проигрыш, self.is_winner будет равен False
        self.received_money = None

        # True если победили мы, False если противник и None если ничья
        self.is_winner = None

        # Минимальная разница в уровне с противником. Эта величина вычитается из текущего уровня персонажа.
        self.min_diff_levels = 0

        # Максимальная разница в уровне с противником. Эта величина добавляется к текущему уровню персонажа.
        # Нельзя нападать на противника, у которого уровень больше трех от нашего
        self.max_diff_levels = 3

    # Сигнал вызывается, когда противник на странице найден -- например, страница загрузилась
    _enemy_load_finished = Signal()

    # Сигнал вызывается, когда противник подходит для нападения
    _enemy_found = Signal()

    def is_ready(self):
        """Возвращает True, если вызов метода run будет иметь смысл -- можем напасть, иначе False."""

        try:
            # TODO: для того, чтобы метод self.fight.is_ready() работал правильно, текущим адресом должны
            # быть Закоулки -- метод has_snikers, используемый в is_ready работает только в Закоулках
            # Идем в Закоулки
            self._mw.alley()

            # TODO: рефакторинг с self._timeout_fight()
            if self._timeout_fight() is not None:
                logger.info('Напасть можно будет через %s секунд.', self._timeout_fight())

            logger.info('self._timeout_fight() = %s.', self._timeout_fight())
            logger.info('self.has_snickers() = %s.', self.has_snickers())
            logger.info('self.is_ready() = %s.', self._timeout_fight() is None or self.has_snickers())

            # True, если таймер закончился или есть Сникерс
            return self._timeout_fight() is None or self.has_snickers()

        except MoswarClosedError:
            raise

        except Exception as e:
            raise MoswarBotError(e)

        return False

    def _timeout_fight(self):
        """Функция возвращает количество оставшихся секунд до возможности напасть.
        Если секунд осталось 0 или меньше 0, то вернется None."""

        for timeout in self._mw.doc.findAll('[id*=timeout]'):
            timer = timeout.attribute('timer')
            if timer and 'alley' in timeout.attribute('href'):
                timer = int(timer)
                return timer if timer > 0 else None

    def run(self):
        """Функция для нападения на игроков.

        Ищем слабого горожанина (заброшенного персонажа) -- не нужно привлекать внимание к боту.
        Уровень противника в пределах нашего +/- 1
        """

        try:
            if self._mw._used:
                logger.warn('Бот в данный момент занят процессом "%s". Выхожу из функции.', self._mw._used_process)
                return

            self._mw._used_process = "Нападение на игроков"
            logger.debug('Выполняю задание "%s".', self._mw._used_process)

            self._mw.alley()

            # TODO: оптимиизровать использование сникерсов -- если они есть, сразу использовать и нападать и так,
            # пока не будут потрачены все

            if not self.is_ready():
                logger.debug('Нападать еще нельзя.')
                return

            self._mw._used = True

            # TODO: если есть тонус, использовать, чтобы сразу напасть
            # TODO: флаг на разрешение использования тонуса, чтобы сразу напасть
            # self.use_tonus()

            # Если не получилось съесть Сникерс, восстанавливаем по старинке
            if not self.eat_snickers():
                if self._mw.current_hp() < self._mw.max_hp():
                    self._mw.restore_hp.run()

            logger.debug('Нажимаю на кнопку "Отнять у слабого".')
            # TODO: в одном из запусков дальше этой строки, похоже дело не пошло, возможно, страница с кнопкой
            # не прогрузилась

            # Кликаем на кнопку "Отнять у слабого"
            self._mw.click_tag(self._css_path_button_fight)

            # Если не нашли подходящего противника, смотрим следующего
            if not self._check_enemy():
                self._timer_next_enemy.start()

                # Ожидаем пока противник не будет найден
                loop = QEventLoop()
                self._enemy_found.connect(loop.quit)
                loop.exec_()

            logger.debug('Нападаем на "%s" [%s]: %s.', self.enemy_name, self.enemy_level, self.enemy_url)

            # Кликаем на кнопку "Напасть"
            self._mw.click_tag('.button-fight a')

            # Перемотка битвы
            forward = '#controls-forward'

            # Ждем пока после клика прогрузится страница и появится элемент
            Waitable(self._mw).wait(forward)

            # Перематываем бой
            self._mw.click_tag(forward)

            # Обрабатываем результаты боя
            self.handle_results()

        except MoswarClosedError:
            raise

        except Exception as e:
            raise MoswarBotError(e)

        finally:
            self._mw._used = False

    def name_winner(self):
        """Функция возвращает имя победителя в драке."""

        try:
            name = self._mw.doc.findFirst('.result div').toPlainText()
            name = name.replace('Победитель:', '')
            name = name[:name.rindex('[')]
            return name.strip()
        except Exception as e:
            raise MoswarElementIsMissError(e)

    def handle_results(self):
        """Обработка результата боя."""

        result = '.result'

        # Ждем пока после клика прогрузится страница и появится элемент
        Waitable(self._mw).wait(result)

        # Найдем элемент, в котором будут все результаты боя
        result = self._mw.doc.findFirst(result)

        if 'Ничья!' in result.toPlainText():
            self.is_winner = None
            self.received_money = 0
            logger.debug('Результат боя: Ничья.')
            return

        # Проверим по именам кто победил
        self.is_winner = self._mw.name() == self.name_winner()

        tugriki = result.findFirst('.tugriki').toPlainText().replace(',', '')
        tugriki = int(tugriki)
        self.received_money = tugriki

        # Сначала покажем выигранные монет и опыт, потом все остальное. Список используется для того, чтобы
        # порядок вывода результата боя был в порядке добавления элементов в этот список
        result_item_keys = ['Монеты', 'Опыт']

        result_dict = {
            'Монеты': tugriki,
            'Опыт': result.findFirst('.expa').toPlainText(),
        }

        neft = result.findFirst('.neft')
        if not neft.isNull():
            result_item_keys.append('Нефть')
            result_dict['Нефть'] = int(neft.toPlainText())

        # Искры не всегда будут -- обычно перед праздниками они появляются
        sparkles = result.findFirst('.sparkles')
        if not sparkles.isNull():
            result_item_keys.append('Искры')
            result_dict['Искры'] = sparkles.toPlainText()

        for img in result.findAll('.object-thumb'):
            obj = img.findFirst('img').attribute('alt')
            count = img.findFirst('.count').toPlainText()

            result_dict[obj] = count
            result_item_keys.append(obj)

        result_list = list()
        for key in result_item_keys:
            result_list.append('  {}: {}'.format(key, result_dict[key]))

        result_str = 'Результат боя:'
        if not self.is_winner:
            result_str += ' Бой проигран. Вся награда достается противнику.'

        result_str += '\n'
        result_str += '\n'.join(result_list)

        logger.debug(result_str)

    # TODO: работает только в Закоулках
    def has_tonus(self):
        """Функция возвратит True, если можно использовать Тонус для сброса таймера, иначе False."""

        button = self._mw.doc.findFirst(self._css_path_button_use_tonus)
        return not button.isNull()

    def use_tonus(self):
        """Функция для использования Тонуса, для сброса таймаута между драками.
        Возвращает True, если получилось, иначе False."""

        if self.has_tonus():
            logger.debug('Использую Тонус.')
            self._mw.click_tag(self._css_path_button_use_tonus)

            # TODO: если Тонуса будет не хватать, то появится окошко с предложением восстановить за плату

            # Ждем пока после клика прогрузится страница и появится элемент
            Waitable(self._mw).wait(self._css_path_button_use_tonus)
            return True

        return False

    # TODO: работает только в Закоулках
    def has_snickers(self):
        """Функция возвратит True, если можно съесть Сникерс, иначе False."""

        button = self._mw.doc.findFirst(self._css_path_button_snikers)
        return not button.isNull()

    def eat_snickers(self):
        """Функция для съедания Сникерса. Возвращает True, если получилось съесть, иначе False."""

        if self.has_snickers():
            logger.debug('Съедаю сникерс.')
            self._mw.click_tag(self._css_path_button_snikers)

            # Ждем пока после клика прогрузится страница и появится элемент
            Waitable(self._mw).wait(self._css_path_button_fight)
            return True

        return False

    def _check_enemy_load(self):
        """Функция для ожидания загрузки страницы с выбором противника."""

        enemy = self._mw.doc.findFirst('.fighter2')

        # Если нашли элемент, описывающий противника
        if not enemy.isNull():
            self._enemy_load_finished.emit()
            self._timer_enemy_load.stop()

    def _check_enemy(self):
        """Функция ищет противника на текущей странице и проверяет его тип и уровень.
        Возвращает True если нашелся подходящий противник, иначе False.

        """

        self._timer_enemy_load.start()

        loop = QEventLoop()
        self._enemy_load_finished.connect(loop.quit)
        loop.exec_()

        enemy = self._mw.doc.findFirst('.fighter2')

        # Определим тип противника -- нам нужен горожанин (нпс)
        is_npc = enemy.findFirst('.npc')
        is_npc = not is_npc.isNull()

        # Узнаем уровень противника
        level = enemy.findFirst('.level')
        level = level.toPlainText()
        level = level.replace('[', '').replace(']', '')
        level = int(level)

        # Гиперссылка на профиль противника
        a = enemy.findFirst('a')

        # Имя противника
        name = a.toPlainText()

        # Адрес противника
        url = urljoin(self._mw.moswar_url, a.attribute('href'))

        my_level = self._mw.level()

        # TODO: добавить ограничение на количество попыток найти гражданина, перед тем как напасть на игрока

        # Проверяем, что уровень противника находится в пределе диапазона
        check_level = my_level - self.min_diff_levels <= level <= my_level + self.max_diff_levels

        found = is_npc and check_level
        if found:
            self.enemy_name = name
            self.enemy_level = level
            self.enemy_url = url

        return found

    def _next_enemy(self):
        """Функция для поиска следующего противника."""

        logger.debug('Ищем следующего противника.')

        # Кликаем на кнопку "Искать другого"
        self._mw.click_tag(".button-search a")

        # Если нашли противника
        if self._check_enemy():
            self._enemy_found.emit()
            self._timer_next_enemy.stop()
        else:
            # Ищем дальше
            self._timer_next_enemy.start()
Beispiel #40
0
class Bar(QToolBar):

    clicked = Signal(str)

    def __init__(self, orientation=Qt.Horizontal, parent=None):
        super().__init__(parent)
        self.setOrientation(orientation)
        policy = QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed,
                             QSizePolicy.ButtonBox)
        policy.setHeightForWidth(True)
        policy.setWidthForHeight(True)
        self.setSizePolicy(policy)
        self.setFloatable(False)
        self.setToolTip("""<p><b>Goto Letter</b></p>
<p>Left-click a letter to navigate to the first entry starting with that
letter.</p>
<p>Right-click a letter to navigate to the first entry starting with the
most recently left-clicked letter, plus any subsequent right-clicked
letters, and this letter.</p>
<p>(One left-click or a fifth right-click clears the previous letters.)</p>
<p>For example, left-click <i>S</i> to go to the first “s” entry. Then
right-click <i>A</i> to go to the first “sa” entry, then right-click
<i>T</i> to go to the first “sat” entry.</p>""")
        self.timer = QTimer(self)
        self.timer.setSingleShot(True)
        self.timer.timeout.connect(self.onTimeout)
        size = self.font().pointSizeF()
        strategy = QFont.StyleStrategy(QFont.PreferOutline
                                       | QFont.PreferAntialias
                                       | QFont.PreferQuality)
        self.mainFont = QFont("courier new")
        self.mainFont.setFixedPitch(True)
        self.mainFont.setStyleStrategy(strategy)
        self.mainFont.setStyleHint(QFont.Monospace)
        self.mainFont.setPointSizeF(size * 1.5)
        self.mainFont.setBold(True)
        self.mainFont.setItalic(False)
        self.setFont(self.mainFont)
        self.smallFont = QFont("helvetica")
        self.smallFont.setStyleStrategy(strategy)
        self.smallFont.setStyleHint(QFont.Times)
        self.smallFont.setPointSizeF(size * 1.25)
        self.smallFont.setBold(False)
        self.smallFont.setItalic(False)
        self.letters = string.ascii_uppercase
        self.word = None

    def sizeHint(self):
        return self.minimumSizeHint()

    def minimumSizeHint(self):
        count = len(self.letters)
        widthAll = self.fontMetrics().width(self.letters) * H_GAP_PC
        widthW = self.fontMetrics().width("WW")
        heightOne = self.fontMetrics().height()
        if self.orientation == Qt.Vertical:
            return QSize(widthW, (heightOne * count) + HANDLE_OFFSET)
        return QSize(widthAll + HANDLE_OFFSET, heightOne * V_GAP_PC)

    def paintEvent(self, event):
        super().paintEvent(event)
        letterPen = QPen(Qt.darkGreen if self.isEnabled() else Qt.lightGray)
        wordPen = QPen(Qt.darkCyan if self.isEnabled() else Qt.lightGray)
        painter = QPainter(self)
        painter.setRenderHints(QPainter.TextAntialiasing)
        painter.setPen(letterPen)
        heightOne = self.fontMetrics().height()
        widthOne = self.fontMetrics().width("W")
        if self.orientation == Qt.Vertical:
            y = HANDLE_OFFSET
            for c in self.letters:
                rect = QRectF(X_OFFSET, y, widthOne, heightOne)
                painter.drawText(rect, Qt.AlignCenter, c)
                y += heightOne
            if self.word is not None:
                painter.setFont(self.smallFont)
                painter.setPen(wordPen)
                rect = self.rect().adjusted(0, HANDLE_OFFSET, -widthOne / 3, 0)
                painter.drawText(rect,
                                 Qt.AlignTop | Qt.AlignRight | Qt.TextWordWrap,
                                 "\n".join(self.word.lower()))
                painter.setPen(letterPen)
                painter.setFont(self.mainFont)
        else:
            widthOne *= H_GAP_PC
            x = HANDLE_OFFSET
            for c in self.letters:
                rect = QRectF(x, 0, widthOne, heightOne * V_GAP_PC)
                painter.drawText(rect, Qt.AlignCenter, c)
                x += widthOne
            if self.word is not None:
                painter.setFont(self.smallFont)
                painter.setPen(wordPen)
                rect = self.rect().adjusted(HANDLE_OFFSET, 0, 0, 0)
                painter.drawText(rect, Qt.AlignBottom | Qt.AlignLeft,
                                 self.word.lower())
                painter.setPen(letterPen)
                painter.setFont(self.mainFont)

    def mousePressEvent(self, event):
        if self.orientation == Qt.Vertical:
            if event.y() < HANDLE_OFFSET:
                event.ignore()
                return
            heightOne = self.fontMetrics().height()
            i = clamp(0, int((event.y() - HANDLE_OFFSET) / heightOne),
                      len(self.letters) - 1)
        else:
            if event.x() < HANDLE_OFFSET:
                event.ignore()
                return
            widthOne = self.fontMetrics().width("W") * H_GAP_PC
            i = clamp(0, int((event.x() - HANDLE_OFFSET) / widthOne),
                      len(self.letters) - 1)
        event.accept()
        word = self.letters[i]
        if event.button() == Qt.LeftButton or self.word is None:
            self.word = word
        elif event.button() == Qt.RightButton:
            if len(self.word) >= 4:
                self.word = word
            elif self.word is not None:
                self.word += word
        if self.word is not None:
            self.clicked.emit(self.word.lower())
            self.timer.start(10000)
        self.update()

    def contextMenuEvent(self, event):
        if ((self.orientation == Qt.Vertical and event.y() < HANDLE_OFFSET) or
            (self.orientation == Qt.Horizontal and event.x() < HANDLE_OFFSET)):
            event.ignore()
        else:
            event.accept()

    def setOrientation(self, orientation):
        self.orientation = orientation
        self.update()

    def heightForWidth(self, width):
        count = len(self.letters)
        heightOne = self.fontMetrics().height()
        widthAll = self.fontMetrics().width(self.letters) * H_GAP_PC
        if widthAll > width:  # Needs to be vertical
            return heightOne * count
        return heightOne * V_GAP_PC  # Needs to be horizontal

    def onTimeout(self):
        self.timer.stop()
        if self.word is not None:
            self.word = None
            self.update()
Beispiel #41
0
class InfoBar(object):
  """Adds a info bar to the bottom of the main window"""

  # Should be stored and restored from uistate.ini
  infoSettings = {"showCPU": True,
                 "showMemory": True,
                 "showDisk": True,
                 "showNetwork": True
                 }
  
  def __init__(self):

    global gEnableResourceMonitoring
    self.enableResourceMonitoring  = gEnableResourceMonitoring
    self.bar = hiero.ui.mainWindow().statusBar()

    self.updateMonitorIntervalMS = gUpdateIntervalMS # The monitor update time in milliseconds.
    self.timer = QTimer()
    self.timer.setSingleShot(False)
    self.timer.timeout.connect(self.updateStatusBar)

    self.currentDiskIOBytes = psutil.disk_io_counters().read_bytes
    self.currentNetworkBytesReceived = psutil.net_io_counters().bytes_recv

    # This observes the current pid (the App process id) via psutil, and reports back
    if self.enableResourceMonitoring:
      self.processHelper = PSUtilProcessWrapper()

    # The frameServer instance
    self.frameServerInstance = nukestudio.frameServer

    # Initialise the Status Bar
    self.setupUI()

    # We haven't started monitoring at this point
    self.isMonitoring = False

    # Begin monitoring after a few secs to give frame server time to start up properly
    QTimer.singleShot(gInitialDelayMS, self.startMonitoring)

  def getLatestStyleSheet(self):
    styleFile = os.path.join( cwd, "style.stylesheet")
    with open(styleFile, "r") as fh:
      return fh.read()

  def hide(self):
    """Hide the info bar"""
    self.bar.setHidden(True)

  def show(self):
    """Show the info bar"""  
    self.bar.setHidden(False)
    
  def rendersExistInQueue(self):
    """Return whether renders exist in the Frame server queue"""
    return len(self.frameServerInstance.renderQueue.requestsInProgress) > 0

  def _handleServerUnreachable(self):
    """This is called when the server becomes unreachable"""
    print "[WARNING]: Nuke Frame Server was not reachable."
    self.frameserverStatusLabel.setPixmap(QPixmap("icons:Offline.png"));
    self.restartServerButton.setHidden(False)  

  def _setResourcesLabelColour(self, memRatio, cpuUsage):

    """Sets the Resources label to be red if the memory usage gets too high"""
    if memRatio > 0.9:
      color  = QColor( Qt.red )
      alpha  = 220
      values = "{r}, {g}, {b}, {a}".format(r = color.red(),
                                           g = color.green(),
                                           b = color.blue(),
                                           a = alpha
                                           )
      #self.nukeResourcesLabel.setStyleSheet("QLabel { vertical-align: middle; font: 10pt; color: rgba("+values+"); }")
    else:
      #self.nukeResourcesLabel.setStyleSheet("QLabel { vertical-align: middle; font: 10pt; }")
      return


  def restartServerAsync(self):
    self.frameServerInstance.stop()
    self.frameServerInstance.start()
    QTimer.singleShot(3000, self.startMonitoring)

  def restartServer(self):
    """Called to restart the Nuke Frame Server, done asynchronously"""
    self.stopMonitoring()
    self.restartServerButton.setHidden(True)
    #self.nukeResourcesLabel.setText("Re-Starting Frame Server...")
    QTimer.singleShot(150, self.restartServerAsync)

  # Displays the resources label, based on settings.
  def buildResourcesLabel(self):
    if self.isMonitoring:
      print "Building resources Label..."

  def updateResourcesStatusLabel(self):
    """Updates the Memory Label String"""

    if self.enableResourceMonitoring:
      if self.memoryString.isVisible():
        self.updateMemoryLayout()

      if self.cpuString.isVisible():
        self.updateCPULayout()

      if self.diskString.isVisible():
        self.updateDiskLayout()

      if self.networkString.isVisible():
        self.updateNetworkLayout()

      #totalSystemMemoryGB = self.processHelper.totalSystemMemory()
      #memRatio = currentMemUsageGB / totalSystemMemoryGB
      # This little test makes the label red if the memory usage exceeds 90% of the maximum allowed
      #self._setResourcesLabelColour( memRatio, currentCPUUsageAsPercentatge )

  def updateMemoryLayout(self):
    currentMemUsageGB = self.processHelper.nukeMemoryUsageInGB()
    currentMemUsageAsPercentage = self.processHelper.nukeMemoryUsageAsPercentage()
    self.memoryString.setText("%.2f GB (%.1f%%)" % (currentMemUsageGB, currentMemUsageAsPercentage))

  def updateCPULayout(self):
    currentCPUUsageAsPercentatge = self.processHelper.nukeCPUUsageAsPercentage()
    self.cpuString.setText("%.1f%%" % currentCPUUsageAsPercentatge)

  def updateDiskLayout(self):
    diskMBPerSec = self._diskMBPerSec()
    self.diskString.setText("%.2f MB/s" % diskMBPerSec)

  def updateNetworkLayout(self):
    networkMBPerSec = self._networkMBPerSec()
    self.networkString.setText("%.2f MB/s" % networkMBPerSec)

  def _diskMBPerSec(self):
    """Returns Total Disk Read+Write speed in MB/s"""
    oldBytes = self.currentDiskIOBytes
    DISKS = psutil.disk_io_counters(perdisk=True)
    readWriteBytes =[(DISKS[disk].read_bytes, DISKS[disk].write_bytes) for disk in DISKS.keys()]
    newBytes = sum([sum(x) for x in zip(*readWriteBytes)])
    bytesDiff = newBytes-oldBytes
    self.currentDiskIOBytes = newBytes
    bytesPerSecond = (newBytes-oldBytes)/(self.updateMonitorIntervalMS/1000)
    MBPerSecond = bytesToMB(bytesPerSecond)
    return MBPerSecond

  def _networkMBPerSec(self):
    """Returns Total Network Read+Write speed in MB/s"""
    oldBytes = self.currentNetworkBytesReceived

    NET = psutil.net_io_counters(pernic=True)
    readWriteBytes =[(NET[adapter].bytes_recv, NET[adapter].bytes_sent) for adapter in NET.keys()]
    newBytes = sum([sum(x) for x in zip(*readWriteBytes)])
    bytesDiff = newBytes-oldBytes
    self.currentNetworkBytesReceived = newBytes
    bytesPerSecond = (newBytes-oldBytes)/(self.updateMonitorIntervalMS/1000)
    MBPerSecond = bytesToMB(bytesPerSecond)
    return MBPerSecond

  def setupUI(self):
    """Initialise the UI"""

    self.bar.setStyleSheet( self.getLatestStyleSheet() )
    #self.bar.setFixedHeight(30)
    self.frameserverStatusLabel = QLabel("")

    # Resources
    self.cpuIconPath = os.path.join(gIconPath, "cpu.png")
    self.memoryIconPath = os.path.join(gIconPath, "memory.png")
    self.diskReadIconPath = os.path.join(gIconPath, "disk_read.png")
    self.networkReadIconPath = os.path.join(gIconPath, "net_read.png")

    # MEMORY SECTION
    self.memoryImageButton = QPushButton(QPixmap((self.memoryIconPath)).scaledToHeight(20, Qt.SmoothTransformation),"")
    self.memoryImageButton.setObjectName("show_button_memory")
    self.memoryImageButton.setToolTip("Click to toggle monitoring of 'Real Memory' usage")
    self.memoryString = QLabel("MEMORY (GB)")
    self.memoryString.setToolTip("'Real Memory' usage of this Nuke Session")
    self.memoryImageButton.clicked.connect(lambda: self.show_button_clicked(self.memoryString))

    # CPU SECTION
    self.cpuImageButton = QPushButton(QPixmap((self.cpuIconPath)).scaledToHeight(20, Qt.SmoothTransformation),"")
    self.cpuImageButton.setObjectName("show_button_cpu")
    self.cpuImageButton.setToolTip("Click to toggle monitoring of CPU usage of this Nuke Session")
    self.cpuString = QLabel("CPU (%)")
    self.cpuString.setToolTip("CPU usage of this Nuke Session")
    self.cpuImageButton.clicked.connect(lambda: self.show_button_clicked(self.cpuString))

    # DISK SECTION
    self.diskImageButton = QPushButton(QPixmap((self.diskReadIconPath)).scaledToHeight(20, Qt.SmoothTransformation),"")
    self.diskImageButton.setObjectName("show_button_disk")
    self.diskImageButton.setToolTip("Click to toggle monitoring of Disk Read+Write usage for this machine")
    self.diskString = QLabel("DISK (MB/s)")
    self.diskImageButton.clicked.connect(lambda: self.show_button_clicked(self.diskString))
    self.diskString.setToolTip("Disk Read+Write usage for this machine")

    # NETWORK SECTION
    self.networkImageButton = QPushButton(QPixmap((self.networkReadIconPath)).scaledToHeight(20, Qt.SmoothTransformation),"")
    self.networkImageButton.setObjectName("show_button_network")
    self.networkImageButton.setToolTip("Click to toggle monitoring of Network Read+Write traffic")
    self.networkString = QLabel("NETWORK (MB/s)")
    self.networkString.setToolTip("Total Network Read+Write traffic for this machine")
    self.networkImageButton.clicked.connect(lambda: self.show_button_clicked(self.networkString))

    # Settings Button - Displays what options should be shown in the Status Bar
    self.settingsButton = QPushButton()
    self.settingsButton.setIcon(QIcon("icons:Settings.png"))
    self.settingsButton.clicked.connect(self.showSettings)

    # Build the layout based on Preferences
    #self.cpuWidget.setVisible(self.infoSettings['showCPU'])
    #self.memoryWidget.setVisible(self.infoSettings['showMemory'])
    #self.diskWidget.setVisible(self.infoSettings['showDisk'])
    #self.networkWidget.setVisible(self.infoSettings['showNetwork'])

    self.restartServerButton = QPushButton(QPixmap("icons:TransformRotateRight.png").scaledToHeight(20, Qt.SmoothTransformation),"")
    self.restartServerButton.setFixedHeight(16)    
    self.restartServerButton.clicked.connect(self.restartServer)
    self.restartServerButton.setHidden(True)
    self.restartServerButton.setFlat(True)
    self.restartServerButton.setToolTip("Click here to restart the Nuke Frameserver")
    self.frameServerIsRendering = False

    self.spinnerMovie = QMovie("icons:RenderingSpinner.gif")
    self.spinnerMovie.start()

    self.bar.addPermanentWidget(self.cpuImageButton)
    self.bar.addPermanentWidget(self.cpuString)
    self.bar.addPermanentWidget(self.memoryImageButton)
    self.bar.addPermanentWidget(self.memoryString)
    self.bar.addPermanentWidget(self.diskImageButton)
    self.bar.addPermanentWidget(self.diskString)
    self.bar.addPermanentWidget(self.networkImageButton)
    self.bar.addPermanentWidget(self.networkString)
    self.bar.addPermanentWidget(self.frameserverStatusLabel)
    self.bar.addPermanentWidget(self.restartServerButton)
    self.bar.addPermanentWidget(self.settingsButton)

  def show_button_clicked(self, sender):
    sender.setVisible(not sender.isVisible())

  def _updateUIForServerIsRunning(self):
    """Updates the UI for when the server is reachable"""
    #self.frameserverStatusLabel.setToolTip("Nuke Frame Server is reachable")
    self.getFrameServerWorkers()
    self.frameserverStatusLabel.setPixmap(QPixmap("icons:OK.png"))
    self.restartServerButton.setHidden(True)

  def showSettings(self):
      dialog = SettingsDialog()
      dialog.show()

  # Returns a nicely formatted list of Frame Server workers
  def getFrameServerWorkers(self):
    statusString = str(self.frameServerInstance.getStatus(1000))
    workers = re.findall("workerStatus \{.*?\}", statusString)

    if len(workers) == 0:
        self.frameserverStatusLabel.setToolTip("Unable to determine number of frame server workers.")
        return

    prettyWorkersString = "Frame Server Status (%i workers):\n" % len(workers) + "\n".join(workers)
    self.frameserverStatusLabel.setToolTip(prettyWorkersString)

  def updateStatusBar(self):
    """Updates the Status bar widgets depending on whether the frameServer is reachable"""

    #print "Status: ", str(self.frameServerInstance.getStatus(10))

    # DEBUG - Stylesheet Changes can be applied here and seen live
    #self.bar.setStyleSheet( self.getLatestStyleSheet() )

    try:
      isRunning = self.frameServerInstance.isRunning(0.25)

      if isRunning and not self.rendersExistInQueue():
        self._updateUIForServerIsRunning()
        self.frameServerIsRendering = False

      elif isRunning and self.rendersExistInQueue():
        if self.frameServerIsRendering == False:
          self.frameServerIsRendering = True
          self.frameserverStatusLabel.setPixmap(None)
          self.frameserverStatusLabel.setMovie(self.spinnerMovie)

      else:
        self._handleServerUnreachable()

      self.updateResourcesStatusLabel()

    except:
      self._handleServerUnreachable()
      self.updateResourcesStatusLabel()

  def startMonitoring(self):
    """This timer fires every X milliseconds to update the status."""
    self.timer.start(self.updateMonitorIntervalMS)
    self.isMonitoring = True

  def stopMonitoring(self):
    """Stops the monitoring process"""
    self.timer.stop()
    self.isMonitoring = False
Beispiel #42
0
class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.setFixedSize(960, 640)
        #不会出现对toolbar的隐藏
        self.toolBar.toggleViewAction().setVisible(False)
        self.finishingLayout()
        self.setupValue()
        self.setupSignal()
        self.setupAction(config=False,
                         start=False,
                         reset=False,
                         result=False,
                         printer=False,
                         back=False)
        self.setShow(1)
        self.setShow(3)

    def setupValue(self):
        self.__action = {
            "config": False,
            "start": False,
            "reset": False,
            "result": False,
            "print": False,
            "back": False,
            "exit": True
        }
        self.color_sheet = [
            '#0000ff', '#ff0000', '#ffff00', '#00ff40', '#00ffff', '#0080c0',
            '#8080c0', '#ff00ff', '#800000', '#ff8000', '#008040', '#800080',
            'black'
        ]
        self.lcd_group = [
            self.lcd_window.press_lcd1, self.lcd_window.press_lcd2,
            self.lcd_window.press_lcd3, self.lcd_window.press_lcd4,
            self.lcd_window.press_lcd5, self.lcd_window.press_lcd6,
            self.lcd_window.digital_lcd1, self.lcd_window.digital_lcd2,
            self.lcd_window.digital_lcd3, self.lcd_window.digital_lcd4,
            self.lcd_window.speed_lcd, self.lcd_window.acceleration_lcd,
            self.lcd_window.shift_lcd
        ]
        self.chk_group = [
            self.lcd_window.press_chk1, self.lcd_window.press_chk2,
            self.lcd_window.press_chk3, self.lcd_window.press_chk4,
            self.lcd_window.press_chk5, self.lcd_window.press_chk6,
            self.lcd_window.digital_chk1, self.lcd_window.digital_chk2,
            self.lcd_window.digital_chk3, self.lcd_window.digital_chk4,
            self.lcd_window.speed_chk, self.lcd_window.acceleration_chk,
            self.lcd_window.shift_chk
        ]
        self.tcp_link = TcpLink()
        #比例转换系数1,5,10
        self.scaleRegion = (40, 10, 5, 2, 1)
        self.scaleFactor = [10, 10, 10, 10, 10, 10]
        self.lines_data = [None] * 14
        self.report = {}
        self.report['PRESS'] = {}
        self.report['DIGITAL'] = {}
        self.ST_flag = False
        self.init_press = 0

    def setupSignal(self):
        self.connect(self.action_exit, SIGNAL("triggered()"), self.actionExit)
        self.connect(self.action_config, SIGNAL("triggered()"),
                     self.actionConfig)
        self.connect(self.action_start, SIGNAL("triggered()"),
                     self.actionStart)
        self.connect(self.action_reset, SIGNAL("triggered()"),
                     self.actionReset)
        self.connect(self.action_result, SIGNAL("triggered()"),
                     self.actionResult)
        self.connect(self.action_print, SIGNAL("triggered()"),
                     self.actionPrint)
        self.connect(self.action_back, SIGNAL("triggered()"), self.actionBack)
        self.connect(self.action_read_report, SIGNAL("triggered()"),
                     self.actionReadReport)
        self.connect(self.notice_window.default_part.connect_button,
                     SIGNAL("clicked()"), self.connectButtonClicked)
        self.connect(self.notice_window.default_part.read_file_button,
                     SIGNAL("clicked()"), self.readFileButtonClicked)
        self.notice_window.current_press.ready_button.clicked.connect(
            self.readyButtonClicked)
        self.lcd_window.st_transfer.clicked.connect(self.setShiftTimeChange)
        #不能在外界直接操作GUI只能采用信号槽方式
        self.tcp_link.signal_tips.connect(self.notice_window.setLblNotice)
        self.tcp_link.signal_noticeText.connect(
            self.notice_window.setNoticeText)
        self.tcp_link.signal_progressBar.connect(self.notice_window.setValue)
        self.tcp_link.signal_lcd.connect(self.setPress6LCDValue)
        self.tcp_link.signal_trigger.connect(self.actionStart)
        self.tcp_link.signal_result.connect(self.connectResult)
        self.tcp_link.signal_timeout.connect(self.dataTimeout)
        #右侧LCD 显示
        self.curve_window.lcd_signal.connect(self.lcdDisplay)
        for i in self.chk_group:
            i.toggled.connect(self.lcdCheck)
        #数据收集阶段定时器,根据具体情况删减
        self.draw_timer = QTimer(self)
        self.connect(self.draw_timer, SIGNAL("timeout()"), self.waitDraw)

    def finishingLayout(self):
        self.left_layout = QVBoxLayout()
        self.right_layout = QHBoxLayout()
        self.notice_window = NoticeWidget(self)
        self.curve_window = CurveWidget(self)
        self.left_layout.addWidget(self.curve_window)
        self.left_layout.addWidget(self.notice_window)
        self.curve_window.hide()
        self.widget_container.setLayout(self.left_layout)
        self.explain_window = Explain(self)
        self.configuration_window = Configuration(self)
        self.lcd_window = LCDGroup(self)
        self.right_layout.addWidget(self.explain_window)
        self.right_layout.addWidget(self.configuration_window)
        self.right_layout.addWidget(self.lcd_window)
        self.configuration_window.hide()
        self.lcd_window.hide()
        self.frame_container.setLayout(self.right_layout)

    def setShow(self, flag):
        """flag must a int"""
        if flag == 1:
            self.curve_window.hide()
            self.notice_window.show()
            self.notice_window.update()
        elif flag == 2:
            #print self.notice_window.geometry()
            self.notice_window.hide()
            self.curve_window.show()
            self.curve_window.update()
            #print self.curve_window.geometry()
            #print self.widget_container.geometry()
        elif flag == 3:
            self.explain_window.show()
            self.explain_window.update()
            self.configuration_window.hide()
            self.lcd_window.hide()
        elif flag == 4:
            self.explain_window.hide()
            self.configuration_window.show()
            self.configuration_window.update()
            self.lcd_window.hide()
        elif flag == 5:
            self.explain_window.hide()
            self.configuration_window.hide()
            self.lcd_window.show()
            self.lcd_window.update()

    def setupAction(self, **kwargs):
        """
        kwargs = {"config":False,"start":False,"reset":False,"result":False,"print":False,"back":False,
                    "exit":False}
        """
        for i in kwargs.keys():
            self.__action[i] = kwargs[i]
        self.action_config.setEnabled(self.__action["config"])
        self.action_start.setEnabled(self.__action["start"])
        self.action_reset.setEnabled(self.__action["reset"])
        self.action_result.setEnabled(self.__action["result"])
        self.action_print.setEnabled(self.__action["printer"])
        self.action_back.setEnabled(self.__action["back"])
        self.action_exit.setEnabled(self.__action["exit"])

    def actionOrder(self, order):
        if order == 'init':
            self.setupAction(config=False,
                             start=False,
                             reset=False,
                             result=False,
                             printer=False,
                             back=False)
            self.setShow(1)
            self.setShow(3)
            self.notice_window.setShow(1)
            self.notice_window.reset()
            self.notice_window.setLblNotice(u"点击[检测连接(Ctrl+Alt+L)]与下位机建立通信连接\
                                 \n点击[读取文档(Ctrl+Alt+R)]读取已保存的数据文档")
            self.action_back.setText(u"首页/断开")
        elif order == 'config':
            self.setupAction(config=True, back=True)
            self.notice_window.setShow(2)
            self.setShow(1)
            self.setShow(4)
            self.notice_window.reset()
            self.notice_window.setLblNotice(u"点击[参数设置(F1)]对要进行测量的项目进行相关参数的设置\n"
                                            u"点击[断开连接(F7)]断开与下位机的通信")
            self.action_back.setText(u"断开连接")
        elif order == 'ready':
            self.setupAction(config=False, reset=True)
            self.setShow(1)
            self.setShow(4)
            self.notice_window.setShow(3)
            self.notice_window.setLblNotice(u"点击[准备测试(Ctrl+Alt+R)]进入测量\n"
                                            u"点击[复位(F3)]对配置信息进行重新配置")
        elif order == 'reset':
            self.setupAction(config=True,
                             start=False,
                             reset=False,
                             result=False,
                             printer=False)
            self.notice_window.setShow(2)
            self.setShow(1)
            self.setShow(4)
            self.notice_window.setLblNotice(u"点击[参数设置(F1)]对要进行测量的项目进行相关参数的设置\
                                 \n点击[断开连接(F7)]断开与下位机的通信")
            self.notice_window.setNoticeText(u"对下次测试进行重新配置")
        elif order == 'trigger':
            self.notice_window.setShow(2)
            self.notice_window.setLblNotice(u'此时请勿进行其他操作,若要停止测试请点击[复位(F3)]')
            self.notice_window.setNoticeText(u'等待触发......')
        elif order == 'start':
            self.setupAction(start=True)
            self.notice_window.setShow(2)
            self.notice_window.setLblNotice(
                u'点击[开始(F2)]进行测量,若要重新测试请点击[复位(F3)]')
            self.notice_window.setNoticeText(u'手动触发方式')
        elif order == 'draw':
            self.setupAction(result=True, printer=True)
        elif order == 'tips':
            pass
        elif order == 'read':
            self.setShow(2)
            self.setShow(5)
            self.setupAction(back=True, result=True)
            self.action_back.setText(u"返回首页")

    def actionConfig(self):
        """根据配置对话框的内容获取相关参数"""
        config_dialog = ConfigDialog(self.tcp_link.getLineState)
        config_dialog.show()
        config_dialog.exec_()
        if config_dialog.getPassFlag():
            self.group_digital = [(i[0].text(), i[1].isChecked())
                                  for i in config_dialog.group_digital]
            self.group_press = [(i[0].text(), i[1].isChecked(),
                                 i[2].currentIndex())
                                for i in config_dialog.group_press]
            self.group_speed_trigger = [
                config_dialog.speed_chk.isChecked(),
                config_dialog.trigger_combo.currentIndex()
            ]
            #测试项目,项目名称,测试人员,测试日期
            self.groupNPTInfo = [
                config_dialog.testProject.currentIndex(),
                config_dialog.projectName_edit.text(),
                config_dialog.person_edit.text(),
                config_dialog.time_edit.text()
            ]
            #测试时长
            self.measure_time = config_dialog.measureTime
            #测试距离
            self.measure_distance = config_dialog.measureDistance
            #set默认通道
            self.default_press = config_dialog.default_press.currentIndex()
            #设置默认膛压通道
            self.notice_window.setDefault(self.default_press)
            self.default_digital = config_dialog.default_digital.currentIndex()
            self.tcp_link.setPressChannel(self.default_press)
            self.tcp_link.setDigitalChannel(self.default_digital)
            self.tcp_link.setThresholdPress(config_dialog.threshold_press)
            self.tcp_link.setCalibration(config_dialog.calibration)
            self.tcp_link.setSpeedFactor(self.groupNPTInfo[0])
            #需要绘制的坐标系
            self.exist_axis = []
            #需要调整宽度的坐标系
            axis_num = []
            for i, j in enumerate(self.group_press):
                if j[1]:
                    self.exist_axis.append(i)
                    axis_num.append((i, j[2]))
                    self.scaleFactor[i] = self.scaleRegion[j[2]]
                    #if j[0]:
                    #self.report['PRESS'][i] = j[0]
                    #else:
                    #self.report['PRESS'][i] = u'通道%d' %(i + 1)
                else:
                    self.scaleFactor[i] = 0
                    #self.report['PRESS'][i] = None
            for i, j in enumerate(self.group_digital):
                if j[1]:
                    self.exist_axis.append(i + 6)
                    #if j[0]:
                    #self.report['DIGITAL'][i] = j[0]
                    #else:
                    #self.report['DIGITAL'][i] = '通道%d' %(i + 1)
                #else:
                #self.report['DIGITAL'][i] = None
            if self.group_speed_trigger[0]:
                self.exist_axis.extend([10, 11, 12])
                if self.groupNPTInfo[0] == 0:
                    axis_num.append((12, 1))
                else:
                    axis_num.append((12, 4))
            self.tcp_link.setFactor(self.scaleFactor[:])
            #print self.scaleFactor
            self.curve_window.produceAxis(self.exist_axis[:])
            for i in (axis_num):
                self.curve_window.setTicker(i[0], i[1])
            #开始根据配置获取需求值并显示当前压力
            self.tcp_link.setTestFlag(False)
            self.tcp_link.setCurrentFlag(True)
            #报告关键值
            #序列号
            self.report['SERIAL'] = self.groupNPTInfo[1]
            _test_type = [u'鱼雷', u'诱饵']
            self.report['TYPE'] = _test_type[self.groupNPTInfo[0]]
            _trigger = [u'自动触发', u'手动触发', u'外触发']
            self.report['TRIGGER'] = _trigger[
                self.group_speed_trigger[1]]  #触发方式
            self.report['PERSON'] = self.groupNPTInfo[2]  #测试人员
            self.report['DATE'] = self.groupNPTInfo[3]  #测试日期
            self.tcp_link.setCurrentFlag(True)
            self.actionOrder('ready')
            #print self.measure_distance
        self.configuration_window.readConfig()

    def actionStart(self):
        #print 'start'
        self.lcd_window.check([i[1] for i in self.group_press],
                              [i[1] for i in self.group_digital],
                              self.group_speed_trigger[0])
        #print self.group_speed_trigger[0]
        self.tcp_link.setTriggerFlag(False)
        self.tcp_link.setClearFlag(False)
        self.setupAction(start=False)
        self.notice_window.reset()
        self.setShow(1)
        self.notice_window.setLblNotice(u'正在接收数据中,若想停止测量可直接点击[复位(F3)]终止本次测量')
        self.notice_window.setShow(2)
        self.notice_window.setNoticeText(u'系统正在测量,请等待......')
        self.notice_window.setValue(1)
        self.draw_count = 0
        self.draw_timer.start(100)
        self.draw_timer.setInterval(100)

    def waitDraw(self):
        self.draw_count += 0.1
        value = 100 / (self.measure_time) * self.draw_count
        self.notice_window.setValue(value)
        if self.draw_count >= self.measure_time:
            self.draw()

    def draw(self):
        #关闭时间
        self.tcp_link.setClearFlag(True)
        self.draw_timer.stop()
        self.notice_window.setNoticeText(u'数据接收完毕,处理中......')

        data = self.tcp_link.getData()
        #若无数据直接返回
        if not data:
            self.actionReset()
            return
        #print len(data), 'total data'
        speed_data = data[15::16]
        self.handle = HandleData(speed_data, self.measure_distance)
        length = len(speed_data)
        #开始运动点num
        num = self.handle.getStartTime()
        shift = self.handle.shift()
        if self.groupNPTInfo[0] == 0:
            speed_out = self.handle.getOutTime(shift)
        else:
            speed_out = self.handle.getStopTime(shift, self.measure_distance)
        #所截取数据的长度
        right_limit = speed_out + 100
        if right_limit > length:
            right_limit = speed_out + 1
        if self.group_speed_trigger[0]:
            x_data = [(i - num) * 5E-4 for i in range(length)]
        else:
            x_data = [i * 5E-4 for i in range(length)]
        self.lines_data[0] = x_data[:right_limit]
        #print self.exist_axis
        for i in range(10):
            y_data = data[i::16]
            self.lines_data[i + 1] = y_data[:right_limit]
            if i < 6:
                if i in self.exist_axis:
                    #不同测试类型取值不同
                    if self.groupNPTInfo[0] == 0:

                        _value = y_data[int(self.measure_time * 2000)]
                        #_value = max(y_data)
                        self.report['PRESS'][i + 1] = '%0.2f' % _value
                        if i == self.default_press:
                            self.report['PRESS'][
                                i + 1] = '%0.2f' % (_value - self.init_press)
                            self.handle.setChamberPressure(
                                self.lines_data[i + 1], self.init_press)
                    else:
                        # _value = y_data[speed_out]
                        _value = y_data[int(self.measure_time * 2000)]
                        self.report['PRESS'][i + 1] = '%0.2f' % _value
                    self.curve_window.drawLine2D(self.exist_axis.index(i),
                                                 self.lines_data[0],
                                                 self.lines_data[i + 1],
                                                 self.color_sheet[i])
                else:
                    self.report['PRESS'][i + 1] = '--'
            else:
                if i in self.exist_axis:
                    begin = self.handle.bleedTime(y_data)
                    #dead = length - begin
                    #self.report['DIGITAL'][i - 5] = ('%0.2f' %(begin*5E-4), '%0.2f' %(dead*5E-4))
                    self.report['DIGITAL'][i - 5] = '%0.1f' % (begin * 5E-4)
                    self.curve_window.drawLine2D(self.exist_axis.index(i),
                                                 self.lines_data[0],
                                                 self.lines_data[i + 1],
                                                 self.color_sheet[i])
                else:
                    self.report['DIGITAL'][i - 5] = ('--')
        self.lines_data[11] = speed_data[:right_limit]
        #加速度
        acceleration = self.handle.acceleration()
        self.lines_data[12] = acceleration[:right_limit]
        self.lines_data[13] = shift[:right_limit]
        if 10 in self.exist_axis:
            self.report['SPEED'] = '%0.1f' % speed_data[speed_out]
            self.curve_window.drawLine2D(self.exist_axis.index(10),
                                         self.lines_data[0],
                                         self.lines_data[11],
                                         self.color_sheet[10])
            self.report['ACCELERATION'] = '%0.1f' % acceleration[speed_out]
            self.curve_window.drawLine2D(self.exist_axis.index(11),
                                         self.lines_data[0],
                                         self.lines_data[12],
                                         self.color_sheet[11])
            self.curve_window.drawLine2D(self.exist_axis.index(12),
                                         self.lines_data[0],
                                         self.lines_data[13],
                                         self.color_sheet[12])
        else:
            self.report['SPEED'] = '--'
            self.report['ACCELERATION'] = '--'
        #图像填充全屏
        self.curve_window.setXFull(self.lines_data[0][0],
                                   self.lines_data[0][-1])
        #延迟时间
        self.report['DELAY'] = '%0.1f' % (num * 5E-4)
        #发射时间
        self.report['SHOOT'] = '%0.1f' % (speed_out * 5E-4)
        #flag = self.report['DIGITAL'].get(self.default_digital + 1, ('--', '--'))
        if self.group_digital[self.default_digital][1]:
            begin, dead = self.handle.getStartClock(
                data[6 + self.default_digital::16])
            #print begin, dead
            #泄放阀开启时间
            self.report['OPEN'] = '%0.1f' % (begin * 5E-4)
            #泄放阀开启时机
            self.report['BLEED'] = '%0.1f' % shift[dead]
        else:
            self.report['OPEN'] = '--'
            self.report['BLEED'] = '--'
        now_time = datetime.datetime.now()
        file_path = now_time.strftime("%Y%m%d%H%M%S")
        if self.groupNPTInfo[0] == 0:
            filename = u'鱼雷' + self.groupNPTInfo[1] + file_path
        else:
            filename = u'诱饵' + self.groupNPTInfo[1] + file_path
        #self.tempHTML(self.group_press, self.group_digital, './template/'+'image.png')
        self.tempPicture("./template/" + "image.png")
        self.saveReport('./report/' + filename + '.pdf')
        self.saveFile('./DigitalSheet/' + filename + '.csv')
        self.curve_window.updateDraw()
        self.curve_window._init_View()
        self.notice_window.setValue(100)
        self.setShow(2)
        self.setShow(5)
        self.actionOrder('draw')

    def tempPicture(self, filename):
        directory = os.path.dirname(filename)
        if not os.path.isdir(directory):
            os.makedirs(directory)
        self.curve_window.canvas.print_figure(filename, dpi=300)

    def saveReport(self, filename):
        directory = os.path.dirname(filename)
        if not os.path.isdir(directory):
            os.makedirs(directory)
        printer = QPrinter()
        printer.setOutputFormat(QPrinter.PdfFormat)
        printer.setOrientation(QPrinter.Landscape)
        printer.setPageSize(QPrinter.A4)
        printer.setOutputFileName(filename)
        if filename:
            self.print_(printer)

    def print_(self, printer):
        painter = QPainter(printer)
        pageRect = printer.pageRect()
        w = pageRect.width() * 0.85
        h = pageRect.height()
        painter.drawPixmap(0, 0, w, h, './template/image.png')

        sansFont = QFont("Helvetica", 10)
        painter.setFont(sansFont)
        fm = QFontMetrics(sansFont)
        height = fm.height() + 10
        vmargin = 40

        x0 = w + 1
        y = 25
        width = fm.width(u"测试编号") + 25
        x1 = x0 + width
        painter.drawText(x0, y, u"报告编号")
        painter.drawText(x1, y, self.report['SERIAL'])

        y += height
        painter.drawText(x0, y, u"测试类型")
        painter.drawText(x1, y, self.report['TYPE'])

        y += height
        painter.drawText(x0, y, u"触发方式")
        painter.drawText(x1, y, self.report['TRIGGER'])

        y += height
        painter.drawText(x0, y, u"测试人员")
        painter.drawText(x1, y, self.report['PERSON'])

        y += height
        painter.drawText(x0, y, u"测试日期")
        painter.drawText(x1, y, self.report['DATE'])

        y += vmargin
        width = fm.width(u"通道1") + 50
        x1 = x0 + width
        space = 0
        painter.drawText(x0 + 20, y, u"压力通道(Mpa)")
        for i, j in enumerate(self.group_press):
            if j[1]:
                y += height
                if j[0]:
                    painter.drawText(x0, y, j[0])
                else:
                    painter.drawText(x0, y, '通道%d'.decode("utf-8") % (i + 1))
                painter.drawText(x1, y, self.report['PRESS'][i + 1])
            else:
                space += height

        y += (vmargin + space)
        width = fm.width(u"通道计量1") + 15
        x1 = x0 + width
        #x2 = x1 + width
        painter.drawText(x0 + 20, y, u"数字量计时通道(s)")
        y += height
        painter.drawText(x0, y, u"通道")
        painter.drawText(x1, y, u"开启时间")
        #painter.drawText(x2, y, u"关闭")
        space = 0
        for i, j in enumerate(self.group_digital):
            if j[1]:
                y += height
                if j[0]:
                    painter.drawText(x0, y, j[0])
                else:
                    painter.drawText(x0, y, '通道%d'.decode("utf-8") % (i + 1))
                painter.drawText(x1, y, self.report['DIGITAL'][i + 1][0])
                #painter.drawText(x2, y, self.report['DIGITAL'][i + 1][1])
            else:
                space += height

        y += (vmargin + space)
        width = fm.width(u"出管速度(m/s)") + 25
        x1 = x0 + width
        painter.drawText(x0, y, u"加速度(g)")
        painter.drawText(x1, y, self.report['ACCELERATION'])

        y += height
        painter.drawText(x0, y, u"出管速度(m/s)")
        painter.drawText(x1, y, self.report['SPEED'])

        y += height
        painter.drawText(x0, y, u"延迟时间(s)")
        painter.drawText(x1, y, self.report['DELAY'])

        y += height
        painter.drawText(x0, y, u"发射时间(s)")
        painter.drawText(x1, y, self.report['SHOOT'])

        y += height
        painter.drawText(x0, y, u"发射深度(s)")
        painter.drawText(x1, y, self.report['DEEP'])

        width = fm.width(u"泄放装置泄放时间(s)") + 5
        y += height
        painter.drawText(x0, y, u"泄放阀开启时机(m)")
        x1 = x0 + width
        painter.drawText(x1, y, self.report['BLEED'])

        y += height
        painter.drawText(x0, y, u"泄放阀开启时间(s)")
        x1 = x0 + width + 1
        painter.drawText(x1, y, self.report['OPEN'])

    def saveFile(self, filename):
        directorys = os.path.dirname(filename)
        if not os.path.isdir(directorys):
            os.makedirs(directorys)
        if not os.path.isfile(filename):
            try:
                file = open(filename, 'w')
            except Exception, e:
                file.close()
                return
            press = '&'.join([self.report['PRESS'][i] for i in range(1, 7)])
            digital = '&'.join(
                [self.report['DIGITAL'][i] for i in range(1, 5)])
            bleed_value = '&'.join([self.report['OPEN'], self.report['BLEED']])
            #20131216对保存的数据进行修改
            datapool = [
                'startest', self.report['SERIAL'], self.report['TYPE'],
                self.report['TRIGGER'], self.report['PERSON'],
                self.report['DATE'], press, digital,
                self.report['ACCELERATION'], self.report['SPEED'],
                self.report['DELAY'], self.report['SHOOT'], bleed_value,
                self.report['DEEP']
            ]
            saveString = self.setText(','.join(datapool))
            #print saveString
            file.write(saveString)
            file.write('\n')
            datapool = []

            datapool.append('0')
            for i in range(6):
                datapool.append('%d' % self.group_press[i][2])
            for i in range(4):
                datapool.append('0')
            datapool.append('0')
            datapool.append('0')
            if self.groupNPTInfo[0] == 0:
                datapool.append('1')
            else:
                datapool.append('4')
            saveString = ','.join(['%s' % i for i in datapool])
            file.write(saveString)
            file.write('\n')
            datapool = []

            datapool.append(True)
            datapool.extend([chk[1] for chk in self.group_press])
            datapool.extend([chk[1] for chk in self.group_digital])
            datapool.extend([self.group_speed_trigger[0] for i in range(3)])
            saveString = ','.join(['%s' % i for i in datapool])
            file.write(saveString)
            file.write('\n')
            datapool = []

            for i, j in enumerate(self.lines_data[0]):
                for k in range(14):
                    datapool.append(self.lines_data[k][i])
                saveString = ','.join(['%s' % v for v in datapool])
                file.write(saveString)
                file.write('\n')
                datapool = []
            file.close()
Beispiel #43
0
class ClimateControlModel(QObject):
    """ Model class representing the climate control system

    Talks with flow, fetches required data from flow, caches some of the data
    """
    __message_received_signal = None
    setting_received = Signal(dict)
    save_setting_result = Signal(dict)
    measurement_changed = Signal(MeasurementEvent)
    relay_status_changed = Signal(RelayStatusEvent)
    device_status_changed = Signal(DeviceStatusEvent)
    controller_status = Signal(str)
    command_response_received = Signal(dict)
    command_sending_result = Signal(dict)
    connection_status = Signal(dict)
    latency_changed = Signal(float)

    def __init__(self):
        """ ClimateControlModel constructor

        Initializes Flow library, thread pool
        """
        super(ClimateControlModel, self).__init__()
        self.__thread_pool = QThreadPool()
        self.__thread_pool.setMaxThreadCount(THREAD_POOL_MAX_THREADS)
        self.__flow_user = None
        self.__controller_device = None
        self.__checking_connection_status = False
        self.__last_measurement_event = None
        self.__last_relay_status_event = None
        self.__last_device_status_event = None
        self.__controller_heartbeat = None
        self.latency = Latency()
        self.__latency_timer = QTimer(self)
        self.__latency_timer.timeout.connect(self.__latency_timer_expired)
        ClimateControlModel.__message_received_signal = MessageReceived()
        ClimateControlModel.__message_received_signal.signal.connect(
            self.__message_received)

    def initialize(self, user_name, password):
        """ Initializes the system

        Initializes flow libraries and connects to server
        Logs in as user and tries to find the controller device, caches if found
        :param str user_name: flow user name
        :param str password: flow user password
        :raises ControllerNotFound, LoginFailed, FlowLibraryError
        """
        flow_url, flow_key, flow_secret = get_server_details()
        try:
            flow_initialize(flow_url, flow_key, flow_secret)
        except FlowException as flow_error:
            if flow_error.connection_error:
                self.check_connection_status()
            LOGGER.exception(flow_error)
            raise FlowLibraryError("Failed to initialize flow library")

        try:
            self.__flow_user = FlowUser(user_name, password)
        except FlowException as flow_error:
            if flow_error.connection_error:
                self.check_connection_status()
            LOGGER.exception(flow_error)
            raise LoginFailed()
        except ValueError as value_error:
            LOGGER.exception(value_error)
            raise LoginFailed()

        try:
            self.__controller_device = self.__flow_user.find_device(
                CONTROLLER_DEVICE_TYPE)
        except FlowException as flow_error:
            if flow_error.connection_error:
                self.check_connection_status()
            LOGGER.exception(flow_error)
            raise ControllerNotFound()

        # enable message reception
        self.__flow_user.enable_message_reception(
            ClimateControlModel.message_received_callback)

    def close(self):
        """ De-initialize the model

        This function won't return till all the worker thread finish their job
        """
        LOGGER.debug("waiting for threads in thread pool")
        self.__thread_pool.waitForDone()
        LOGGER.debug("thread pool exit done")
        if self.__flow_user:
            self.__flow_user.logout()

    def get_settings(self):
        """ Get setting for the controller device

        Schedules a worker thread for getting setting
        """
        runnable = WorkerThread(get_setting_work,
                                self.__settings_received,
                                device=self.__controller_device,
                                key=ControllerSetting.controller_config_key)
        self.__thread_pool.start(runnable)

    def save_settings(self, setting):
        """ Save setting of controller device

        :param ControllerSetting setting: setting object to be saved in KVS
        """
        setting_xml = setting.to_xml()
        LOGGER.debug("Trying to save setting xml = {}".format(setting_xml))
        runnable = WorkerThread(save_setting_work,
                                self.__save_settings_result,
                                device=self.__controller_device,
                                key=ControllerSetting.controller_config_key,
                                value=setting_xml)
        self.__thread_pool.start(runnable)

    def send_command(self, command):
        """ Send command to the controller device

        :param ControllerCommandEnum command: command to be sent
        """
        if command not in ControllerCommandEnum:
            raise InvalidCommand("Command not supported")

        LOGGER.info("sending command {}".format(command.value))
        command_xml = ControllerCommand(command).xml
        LOGGER.debug("command xml {}".format(command_xml))
        runnable = WorkerThread(send_command_work,
                                self.__command_sent_result,
                                user=self.__flow_user,
                                device=self.__controller_device,
                                message=command_xml)
        self.__thread_pool.start(runnable)

    def check_connection_status(self):
        """ Starts worker thread for checking connection status
        """
        if not self.__checking_connection_status:
            self.__checking_connection_status = True
            runnable = WorkerThread(check_connection_status_work,
                                    self.__connection_status_result)
            self.__thread_pool.start(runnable)

    @Slot(str, str)
    def __settings_received(self, setting, error):
        """ Slot function called when get setting worker thread finishes the task

        :param str setting: received setting from flow
        :param str error: error string if any
        """
        parsed_setting = None
        if not error:
            LOGGER.debug("Received setting xml = {}".format(setting))
            try:
                parsed_setting = ControllerSetting(setting_xml=setting)
                if not self.__controller_heartbeat:
                    # start heartbeat timer with twice the heartbeat period so that even if some
                    # heartbeat message failed to receive, it should wait for another
                    # heartbeat message
                    self.__controller_heartbeat = \
                        HeartBeatTimer(parsed_setting.controller_heartbeat*2*1000,
                                       self.__heartbeat_timer_status_changed)
                else:
                    # if heartbeat already exist then restart with new time
                    self.__controller_heartbeat.change_timeout_period(
                        parsed_setting.controller_heartbeat * 2 * 1000)
            except ValueError as value_error:
                LOGGER.exception(value_error)
                error = "Invalid setting XML received"
        else:
            if error["connection_error"]:
                self.check_connection_status()

        self.setting_received.emit({"setting": parsed_setting, "error": error})

    @Slot(str)
    def __save_settings_result(self, error=None):
        """ Slot function for the save setting worker function result

        :param str error: error string if any
        """
        self.save_setting_result.emit({"error": error})
        if error:
            LOGGER.debug("Save setting failed: {}".format(error))
            if error["connection_error"]:
                self.check_connection_status()
        else:
            # if KVS updated then send message to device to update the settings
            self.send_command(ControllerCommandEnum.retrieve_settings)
            LOGGER.debug("Setting saved to KVS")

    @Slot(bool, bool)
    def __connection_status_result(self, network, internet):
        """ Slot function for connection status result
        :param bool network: False if network is down
        :param bool internet: False if internet is down
        """
        self.__checking_connection_status = False
        connection_status = {"network": network, "internet": internet}
        self.connection_status.emit(connection_status)

    @Slot(str)
    def __command_sent_result(self, error=None):
        """ Slot function called at the end of sending command

        :param str error: error string if any
        """
        if error:
            self.command_sending_result.emit({"error": error})
            LOGGER.debug("Message sending failed: {}".format(error))
            if error["connection_error"]:
                self.check_connection_status()
        else:
            LOGGER.debug("Message sending success")

# pylint: disable=invalid-name

    @Slot(str)
    def __heartbeat_timer_status_changed(self, status):
        """ Slot function called when heartbeat timer changes its status(start, expire etc)

        It receives controller status. on timer expire-controller status = "OFFLINE"
        on timer refresh(start)- controller status = "ONLINE"
        :param str status: "ONLINE" or "OFFLINE" status
        """
        self.controller_status.emit(status)
        if status == "OFFLINE":
            # timer expired so there might be something wrong with the
            # network or internet so check connection status
            self.check_connection_status()
            LOGGER.debug("Latency timer stopped")
            self.__latency_timer.stop()
            if self.__last_device_status_event:
                # if controller is OFFLINE, make sensor and actuator also OFFLINE
                self.__last_device_status_event.sensor_alive = False
                self.__last_device_status_event.actuator_alive = False
                self.device_status_changed.emit(
                    self.__last_device_status_event)
        else:
            self.__latency_timer.start(LATENCY_PERIOD * 1000)
            LOGGER.debug("Latency timer started")
# pylint: enable=invalid-name

    @Slot()
    def __latency_timer_expired(self):
        """ Slot called on the expiry of latency timer

        """
        # send ping message to controller and calculate latency based on when it is received
        self.send_command(ControllerCommandEnum.ping)

    def __handle_command_response(self, response_dict):
        """ Parse the received response and emit signal if valid response found

        :param dict response_dict: response xml dictionary
        """
        try:
            response = ControllerResponse(response_dict)
            LOGGER.info("received response {}".format(response.response.value))
            if response.response == ControllerResponseEnum.ping:
                # round_trip_time is the difference between current local time and timestamp
                # when message was sent, this includes any processing delay on controller side
                round_trip_time = (datetime.datetime.utcnow() -
                                   response.sent_time).total_seconds()
                LOGGER.debug("round_trip_time: {}".format(round_trip_time))
                # Ignore value where round_trip_time > MAX_LATENCY_TIME
                if round_trip_time <= MAX_LATENCY_VALUE:
                    latency = self.latency.calculate_exponential_moving_average(
                        round_trip_time)
                else:
                    latency = round_trip_time
                self.latency_changed.emit(latency)
                LOGGER.debug("Latency: {}".format(latency))
            else:
                self.command_response_received.emit(
                    {"response": response.response})

            # if retrieve_settings_success is received, get the settings again as it might
            # be update by some other app
            # for e.g. admin app updating threshold and display app updating its values accordingly
            if response.response == ControllerResponseEnum.retrieve_settings_success:
                self.get_settings()
        except ValueError as error:
            LOGGER.exception(error)

    def __process_heartbeat_event(self, event):
        """ Processes HeartBeat event data

        :param HeartBeatEvent event: HeartBeat event object
        """
        if self.__last_measurement_event:
            if not event.measurement_data == self.__last_measurement_event:
                self.measurement_changed.emit(event.measurement_data)
                self.__last_measurement_event = event.measurement_data
            else:
                LOGGER.debug("Ignoring measurement data")
        else:
            self.measurement_changed.emit(event.measurement_data)
            self.__last_measurement_event = event.measurement_data

        LOGGER.debug("Heartbeat Temp: {} Humidity: {} ".format(
            event.measurement_data.temperature,
            event.measurement_data.humidity))

        if self.__last_relay_status_event:
            if not event.relay_status == self.__last_relay_status_event:
                self.relay_status_changed.emit(event.relay_status)
                self.__last_relay_status_event = event.relay_status
            else:
                LOGGER.debug("Ignoring relay status data")
        else:
            self.relay_status_changed.emit(event.relay_status)
            self.__last_relay_status_event = event.relay_status

        if self.__last_device_status_event:
            if not event.device_status == self.__last_device_status_event:
                self.device_status_changed.emit(event.device_status)
                self.__last_device_status_event = event.device_status
            else:
                LOGGER.debug("Ignoring device status data")
        else:
            self.device_status_changed.emit(event.device_status)
            self.__last_device_status_event = event.device_status

        LOGGER.debug("Heartbeat Temp: {} Humidity: {} "
                     "Relay 1 ON: {} Relay 2 ON: {} "
                     "Sensor: {} Actuator: {}".format(
                         event.measurement_data.temperature,
                         event.measurement_data.humidity,
                         event.relay_status.relay_1_on,
                         event.relay_status.relay_2_on,
                         event.device_status.sensor_alive,
                         event.device_status.actuator_alive))

    def __handle_event(self, message_dict):
        """ Create event object according to event type and handle the event

        :param dict message_dict: message content in the dictionary format
        """
        try:
            event = ControllerEventFactory.create_event(message_dict)
            if self.__controller_heartbeat:
                self.__controller_heartbeat.refresh_timer()

            if isinstance(event, MeasurementEvent):
                self.measurement_changed.emit(event)
                self.__last_measurement_event = event
                LOGGER.debug("Measurement Temp: {} Humidity: {}".format(
                    event.temperature, event.humidity))
            elif isinstance(event, RelayStatusEvent):
                self.relay_status_changed.emit(event)
                self.__last_relay_status_event = event
                LOGGER.debug(
                    "RelayStatus Relay 1 ON: {} Relay 2 ON: {}".format(
                        event.relay_1_on, event.relay_2_on))
            elif isinstance(event, DeviceStatusEvent):
                self.device_status_changed.emit(event)
                self.__last_device_status_event = event
                LOGGER.debug("DeviceStatus Sensor: {} Actuator: {}".format(
                    event.sensor_alive, event.actuator_alive))
            elif isinstance(event, HeartBeatEvent):
                self.__process_heartbeat_event(event)

        except ValueError as value_error:
            LOGGER.exception(value_error)

    @Slot(str)
    def __message_received(self, message_content):
        """ Slot function called when message is received by callback function

        This function parses message_content(xml_string) into dictionary
        :param str message_content: message content which is xml string
        """
        LOGGER.debug("Received message {}".format(message_content))
        # We have received a message from controller means network and internet are up
        status = {"network": True, "internet": True}
        self.connection_status.emit(status)
        try:
            message_dict = xmltodict.parse(message_content)
            root_tag = message_dict.keys()[0]
            if root_tag == "response":
                self.__handle_command_response(message_dict)
            elif root_tag == "event":
                self.__handle_event(message_dict)
            else:
                LOGGER.error(
                    "Unsupported message received {}".format(message_content))
        except (ExpatError, KeyError) as error:
            LOGGER.exception("Error in parsing xml {} xml={}".format(
                error.message, message_content))

    @staticmethod
    def message_received_callback(flow_message):
        """ Callback called by library, libflow expects this to be a static class method

        :param flow_message: message object from library
        """
        message = FlowMessage(flow_message)
        message_content = message.get_message_content()
        ClimateControlModel.__message_received_signal.signal.emit(
            message_content)
Beispiel #44
0
class _CompletionList(QListView):
    """Completion list widget
    """
    closeMe = pyqtSignal()
    itemSelected = pyqtSignal(int)
    tabPressed = pyqtSignal()
    
    _MAX_VISIBLE_ROWS = 20  # no any technical reason, just for better UI
    
    _ROW_MARGIN = 6
    
    def __init__(self, qpart, model):
        QListView.__init__(self, qpart.viewport())
        self.setItemDelegate(HTMLDelegate(self))
        
        self._qpart = qpart
        self.setFont(qpart.font())
        
        self.setCursor(QCursor(Qt.PointingHandCursor))
        self.setFocusPolicy(Qt.NoFocus)
        
        self.setModel(model)
        
        self._selectedIndex = -1
        
        # if cursor moved, we shall close widget, if its position (and model) hasn't been updated
        self._closeIfNotUpdatedTimer = QTimer()
        self._closeIfNotUpdatedTimer.setInterval(200)
        self._closeIfNotUpdatedTimer.setSingleShot(True)

        self._closeIfNotUpdatedTimer.timeout.connect(self._afterCursorPositionChanged)
        
        qpart.installEventFilter(self)
        
        qpart.cursorPositionChanged.connect(self._onCursorPositionChanged)
        
        self.clicked.connect(lambda index: self.itemSelected.emit(index.row()))
        
        self.updateGeometry()
        self.show()
        
        qpart.setFocus()
    
    def __del__(self):
        """Without this empty destructor Qt prints strange trace
            QObject::startTimer: QTimer can only be used with threads started with QThread
        when exiting
        """
        pass
    
    def del_(self):
        """Explicitly called destructor.
        Removes widget from the qpart
        """
        self._closeIfNotUpdatedTimer.stop()
        self._qpart.removeEventFilter(self)
        self._qpart.cursorPositionChanged.disconnect(self._onCursorPositionChanged)
        
        # if object is deleted synchronously, Qt crashes after it on events handling
        QTimer.singleShot(0, lambda: self.setParent(None))

    def sizeHint(self):
        """QWidget.sizeHint implementation
        Automatically resizes the widget according to rows count
        
        FIXME very bad algorithm. Remove all this margins, if you can
        """
        width = max([self.fontMetrics().width(word) \
                        for word in self.model().words])
        width = width * 1.4  # FIXME bad hack. invent better formula
        width += 30  # margin
        
        # drawn with scrollbar without +2. I don't know why
        rowCount = min(self.model().rowCount(), self._MAX_VISIBLE_ROWS)
        height = (self.sizeHintForRow(0) * rowCount) + self._ROW_MARGIN

        return QSize(width, height)

    def minimumHeight(self):
        """QWidget.minimumSizeHint implementation
        """
        return self.sizeHintForRow(0) + self._ROW_MARGIN

    def _horizontalShift(self):
        """List should be plased such way, that typed text in the list is under
        typed text in the editor
        """
        strangeAdjustment = 2  # I don't know why. Probably, won't work on other systems and versions
        return self.fontMetrics().width(self.model().typedText()) + strangeAdjustment

    def updateGeometry(self):
        """Move widget to point under cursor
        """
        WIDGET_BORDER_MARGIN = 5
        SCROLLBAR_WIDTH = 30  # just a guess
        
        sizeHint = self.sizeHint()
        width = sizeHint.width()
        height = sizeHint.height()

        cursorRect = self._qpart.cursorRect()
        parentSize = self.parentWidget().size()
        
        spaceBelow = parentSize.height() - cursorRect.bottom() - WIDGET_BORDER_MARGIN
        spaceAbove = cursorRect.top() - WIDGET_BORDER_MARGIN
        
        if height <= spaceBelow or \
           spaceBelow > spaceAbove:
            yPos = cursorRect.bottom()
            if height > spaceBelow and \
               spaceBelow > self.minimumHeight():
                height = spaceBelow
                width = width + SCROLLBAR_WIDTH
        else:
            if height > spaceAbove and \
               spaceAbove > self.minimumHeight():
                height = spaceAbove
                width = width + SCROLLBAR_WIDTH
            yPos = max(3, cursorRect.top() - height)

        xPos = cursorRect.right() - self._horizontalShift()
        
        if xPos + width + WIDGET_BORDER_MARGIN > parentSize.width():
            xPos = max(3, parentSize.width() - WIDGET_BORDER_MARGIN - width)
        
        self.setGeometry(xPos, yPos, width, height)
        self._closeIfNotUpdatedTimer.stop()
    
    def _onCursorPositionChanged(self):
        """Cursor position changed. Schedule closing.
        Timer will be stopped, if widget position is being updated
        """
        self._closeIfNotUpdatedTimer.start()

    def _afterCursorPositionChanged(self):
        """Widget position hasn't been updated after cursor position change, close widget
        """
        self.closeMe.emit()

    def eventFilter(self, object, event):
        """Catch events from qpart
        Move selection, select item, or close themselves
        """
        if event.type() == QEvent.KeyPress and event.modifiers() == Qt.NoModifier:
            if event.key() == Qt.Key_Escape:
                self.closeMe.emit()
                return True
            elif event.key() == Qt.Key_Down:
                if self._selectedIndex + 1 < self.model().rowCount():
                    self._selectItem(self._selectedIndex + 1)
                return True
            elif event.key() == Qt.Key_Up:
                if self._selectedIndex - 1 >= 0:
                    self._selectItem(self._selectedIndex - 1)
                return True
            elif event.key() in (Qt.Key_Enter, Qt.Key_Return):
                if self._selectedIndex != -1:
                    self.itemSelected.emit(self._selectedIndex)
                    return True
            elif event.key() == Qt.Key_Tab:
                self.tabPressed.emit()
                return True
        elif event.type() == QEvent.FocusOut:
            self.closeMe.emit()

        return False

    def _selectItem(self, index):
        """Select item in the list
        """
        self._selectedIndex = index
        self.setCurrentIndex(self.model().createIndex(index, 0))
Beispiel #45
0
class RunnerDialog(QDialog):

    options_added = Signal(Options)
    options_running = Signal(Options)
    options_simulated = Signal(Options)
    options_error = Signal(Options, Exception)
    results_saved = Signal(Results, str)
    results_error = Signal(Results, Exception)

    def __init__(self, parent=None):
        QDialog.__init__(self, parent)
        self.setWindowTitle('Runner')
        self.setMinimumWidth(750)

        # Runner
        self._runner = None

        self._running_timer = QTimer()
        self._running_timer.setInterval(500)

        # Widgets
        self._dlg_progress = QProgressDialog()
        self._dlg_progress.setRange(0, 100)
        self._dlg_progress.setModal(True)
        self._dlg_progress.hide()

        lbl_outputdir = QLabel("Output directory")
        self._txt_outputdir = DirBrowseWidget()

        max_workers = cpu_count() #@UndefinedVariable
        lbl_workers = QLabel('Number of workers')
        self._spn_workers = QSpinBox()
        self._spn_workers.setRange(1, max_workers)
        self._spn_workers.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        lbl_max_workers = QLabel('(max: %i)' % max_workers)

        self._chk_overwrite = QCheckBox("Overwrite existing results in output directory")
        self._chk_overwrite.setChecked(True)

        self._lbl_available = QLabel('Available')
        self._lst_available = QListView()
        self._lst_available.setModel(_AvailableOptionsListModel())
        self._lst_available.setSelectionMode(QListView.SelectionMode.MultiSelection)

        tlb_available = QToolBar()
        spacer = QWidget()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        tlb_available.addWidget(spacer)
        act_open = tlb_available.addAction(getIcon("document-open"), "Open")
        act_open.setShortcut(QKeySequence.Open)
        tlb_available.addSeparator()
        act_remove = tlb_available.addAction(getIcon("list-remove"), "Remove")
        act_clear = tlb_available.addAction(getIcon("edit-clear"), "Clear")

        self._btn_addtoqueue = QPushButton(getIcon("go-next"), "")
        self._btn_addtoqueue.setToolTip("Add to queue")
        self._btn_addtoqueue.setEnabled(False)

        self._btn_addalltoqueue = QPushButton(getIcon("go-last"), "")
        self._btn_addalltoqueue.setToolTip("Add all to queue")
        self._btn_addalltoqueue.setEnabled(False)

        self._lbl_options = QLabel('Queued/Running/Completed')
        self._tbl_options = QTableView()
        self._tbl_options.setModel(_StateOptionsTableModel())
        self._tbl_options.setItemDelegate(_StateOptionsItemDelegate())
        self._tbl_options.setSelectionMode(QListView.SelectionMode.NoSelection)
        self._tbl_options.setColumnWidth(1, 60)
        self._tbl_options.setColumnWidth(2, 80)
        header = self._tbl_options.horizontalHeader()
        header.setResizeMode(0, QHeaderView.Interactive)
        header.setResizeMode(1, QHeaderView.Fixed)
        header.setResizeMode(2, QHeaderView.Fixed)
        header.setResizeMode(3, QHeaderView.Stretch)

        self._btn_start = QPushButton(getIcon("media-playback-start"), "Start")

        self._btn_cancel = QPushButton("Cancel")
        self._btn_cancel.setEnabled(False)

        self._btn_close = QPushButton("Close")

        self._btn_import = QPushButton("Import")
        self._btn_import.setEnabled(False)

        # Layouts
        layout = QVBoxLayout()

        sublayout = QGridLayout()
        sublayout.addWidget(lbl_outputdir, 0, 0)
        sublayout.addWidget(self._txt_outputdir, 0, 1)
        sublayout.addWidget(lbl_workers, 1, 0)

        subsublayout = QHBoxLayout()
        subsublayout.addWidget(self._spn_workers)
        subsublayout.addWidget(lbl_max_workers)
        sublayout.addLayout(subsublayout, 1, 1)
        layout.addLayout(sublayout)

        sublayout.addWidget(self._chk_overwrite, 2, 0, 1, 3)

        sublayout = QGridLayout()
        sublayout.setColumnStretch(0, 1)
        sublayout.setColumnStretch(2, 3)
        sublayout.addWidget(self._lbl_available, 0, 0)
        sublayout.addWidget(self._lst_available, 1, 0)
        sublayout.addWidget(tlb_available, 2, 0)

        subsublayout = QVBoxLayout()
        subsublayout.addStretch()
        subsublayout.addWidget(self._btn_addtoqueue)
        subsublayout.addWidget(self._btn_addalltoqueue)
        subsublayout.addStretch()
        sublayout.addLayout(subsublayout, 1, 1)

        sublayout.addWidget(self._lbl_options, 0, 2)
        sublayout.addWidget(self._tbl_options, 1, 2)
        layout.addLayout(sublayout)

        sublayout = QHBoxLayout()
        sublayout.addStretch()
        sublayout.addWidget(self._btn_import)
        sublayout.addWidget(self._btn_start)
        sublayout.addWidget(self._btn_cancel)
        sublayout.addWidget(self._btn_close)
        layout.addLayout(sublayout)

        self.setLayout(layout)

        # Signal
        self._running_timer.timeout.connect(self._onRunningTimer)

        act_open.triggered.connect(self._onOpen)
        act_remove.triggered.connect(self._onRemove)
        act_clear.triggered.connect(self._onClear)

        self._btn_addtoqueue.released.connect(self._onAddToQueue)
        self._btn_addalltoqueue.released.connect(self._onAddAllToQueue)
        self._btn_start.released.connect(self._onStart)
        self._btn_cancel.released.connect(self._onCancel)
        self._btn_close.released.connect(self._onClose)
        self._btn_import.released.connect(self._onImport)

        self.options_added.connect(self._onOptionsAdded)
        self.options_running.connect(self._onOptionsRunning)
        self.options_simulated.connect(self._onOptionsSimulated)
        self.options_error.connect(self._onOptionsError)
        self.results_error.connect(self._onResultsError)

        # Defaults
        settings = get_settings()
        section = settings.add_section('gui')
        if hasattr(section, 'outputdir'):
            self._txt_outputdir.setPath(section.outputdir)
        if hasattr(section, 'maxworkers'):
            self._spn_workers.setValue(int(section.maxworkers))
        if hasattr(section, 'overwrite'):
            state = True if section.overwrite.lower() == 'true' else False
            self._chk_overwrite.setChecked(state)

    def _onDialogProgressProgress(self, progress, status):
        self._dlg_progress.setValue(progress * 100)
        self._dlg_progress.setLabelText(status)

    def _onDialogProgressCancel(self):
        self._dlg_progress.hide()
        if self._options_reader_thread is None:
            return
        self._options_reader_thread.cancel()
        self._options_reader_thread.quit()
        self._options_reader_thread.wait()

    def _onDialogProgressException(self, ex):
        self._dlg_progress.hide()
        self._options_reader_thread.quit()
        self._options_reader_thread.wait()
        messagebox.exception(self, ex)

    def _onRunningTimer(self):
        self._tbl_options.model().reset()

    def _onOpen(self):
        settings = get_settings()
        curdir = getattr(settings.gui, 'opendir', os.getcwd())

        filepath, namefilter = \
            QFileDialog.getOpenFileName(self, "Open", curdir,
                                        'Options [*.xml] (*.xml)')

        if not filepath or not namefilter:
            return
        settings.gui.opendir = os.path.dirname(filepath)

        if not filepath.endswith('.xml'):
            filepath += '.xml'

        self._options_reader_thread = _OptionsReaderWrapperThread(filepath)
        self._dlg_progress.canceled.connect(self._onDialogProgressCancel)
        self._options_reader_thread.resultReady.connect(self._onOpened)
        self._options_reader_thread.progressUpdated.connect(self._onDialogProgressProgress)
        self._options_reader_thread.exceptionRaised.connect(self._onDialogProgressException)
        self._options_reader_thread.start()

        self._dlg_progress.reset()
        self._dlg_progress.show()

    def _onOpened(self, options):
        self._dlg_progress.hide()
        self._options_reader_thread.quit()
        self._options_reader_thread.wait()
        self._options_reader_thread = None

        try:
            self._lst_available.model().addOptions(options)
        except Exception as ex:
            messagebox.exception(self, ex)

    def _onRemove(self):
        selection = self._lst_available.selectionModel().selection().indexes()
        if len(selection) == 0:
            QMessageBox.warning(self, "Queue", "Select an options")
            return

        model = self._lst_available.model()
        for row in sorted(map(methodcaller('row'), selection), reverse=True):
            model.popOptions(row)

    def _onClear(self):
        self._lst_available.model().clearOptions()

    def _onAddToQueue(self):
        selection = self._lst_available.selectionModel().selection().indexes()
        if len(selection) == 0:
            QMessageBox.warning(self, "Queue", "Select an options")
            return

        model = self._lst_available.model()
        for row in sorted(map(methodcaller('row'), selection), reverse=True):
            options = model.options(row)
            try:
                self._runner.put(options)
            except Exception as ex:
                messagebox.exception(self, ex)
                return

    def _onAddAllToQueue(self):
        model = self._lst_available.model()
        for row in reversed(range(0, model.rowCount())):
            options = model.options(row)
            try:
                self._runner.put(options)
            except Exception as ex:
                messagebox.exception(self, ex)
                return

    def _onStart(self):
        outputdir = self._txt_outputdir.path()
        if not outputdir:
            QMessageBox.critical(self, 'Start', 'Missing output directory')
            return
        max_workers = self._spn_workers.value()
        overwrite = self._chk_overwrite.isChecked()
        self.start(outputdir, overwrite, max_workers)

    def _onCancel(self):
        self.cancel()

    def _onClose(self):
        if self._runner is not None:
            self._runner.close()
        self._running_timer.stop()
        self.close()

    def _onImport(self):
        list_options = self._lst_available.model().listOptions()
        if not list_options:
            return

        # Select options
        dialog = _OptionsSelector(list_options)
        if not dialog.exec_():
            return
        options = dialog.options()

        # Start importer
        outputdir = self._runner.outputdir
        max_workers = self._runner.max_workers
        importer = LocalImporter(outputdir, max_workers)

        importer.start()
        importer.put(options)

        self._dlg_progress.show()
        try:
            while importer.is_alive():
                if self._dlg_progress.wasCanceled():
                    importer.cancel()
                    break
                self._dlg_progress.setValue(importer.progress * 100)
        finally:
            self._dlg_progress.hide()

    def _onOptionsAdded(self, options):
        logging.debug('runner: optionsAdded')
        self._tbl_options.model().addOptions(options)

    def _onOptionsRunning(self, options):
        logging.debug('runner: optionsRunning')
        self._tbl_options.model().resetOptions(options)

    def _onOptionsSimulated(self, options):
        logging.debug('runner: optionsSimulated')
        self._tbl_options.model().resetOptions(options)

    def _onOptionsError(self, options, ex):
        logging.debug('runner: optionsError')
        self._tbl_options.model().resetOptions(options)

    def _onResultsError(self, results, ex):
        logging.debug('runner: resultsError')
        self._tbl_options.model().reset()

    def closeEvent(self, event):
        if self.is_running():
            message = 'Runner is running. Do you want to continue?'
            answer = QMessageBox.question(self, 'Runner', message,
                                          QMessageBox.Yes | QMessageBox.No)
            if answer == QMessageBox.No:
                event.ignore()
                return

        self.cancel()
        self._dlg_progress.close()

        settings = get_settings()
        section = settings.add_section('gui')

        path = self._txt_outputdir.path()
        if path:
            section.outputdir = path
        section.maxworkers = str(self._spn_workers.value())
        section.overwrite = str(self._chk_overwrite.isChecked())

        settings.write()

        event.accept()

    def addAvailableOptions(self, options):
        self._lst_available.model().addOptions(options)

    def removeAvailableOptions(self, options):
        self._lst_available.model().removeOptions(options)

    def clearAvailableOptions(self):
        self._lbl_available.model().clearOptions()

    def start(self, outputdir, overwrite, max_workers):
        self._runner = LocalRunner(outputdir=outputdir,
                                   overwrite=overwrite,
                                   max_workers=max_workers)

        self._tbl_options.setModel(_StateOptionsTableModel(self._runner))

        self._spn_workers.setEnabled(False)
        self._txt_outputdir.setEnabled(False)
        self._chk_overwrite.setEnabled(False)
        self._btn_addtoqueue.setEnabled(True)
        self._btn_addalltoqueue.setEnabled(True)
        self._btn_start.setEnabled(False)
        self._btn_cancel.setEnabled(True)
        self._btn_close.setEnabled(False)
        self._btn_import.setEnabled(True)

        self._runner.options_added.connect(self.options_added.emit)
        self._runner.options_running.connect(self.options_running.emit)
        self._runner.options_simulated.connect(self.options_simulated.emit)
        self._runner.options_error.connect(self.options_error.emit)
        self._runner.results_saved.connect(self.results_saved.emit)
        self._runner.results_error.connect(self.results_error.emit)

        self._running_timer.start()
        self._runner.start()

    def cancel(self):
        if self._runner is None:
            return
        self._runner.cancel()
        self._running_timer.stop()

        self._runner.options_added.disconnect(self.options_added.emit)
        self._runner.options_running.disconnect(self.options_running.emit)
        self._runner.options_simulated.disconnect(self.options_simulated.emit)
        self._runner.options_error.disconnect(self.options_error.emit)
        self._runner.results_saved.disconnect(self.results_saved.emit)
        self._runner.results_error.disconnect(self.results_error.emit)

        self._runner = None

        self._spn_workers.setEnabled(True)
        self._txt_outputdir.setEnabled(True)
        self._chk_overwrite.setEnabled(True)
        self._btn_addtoqueue.setEnabled(False)
        self._btn_addalltoqueue.setEnabled(False)
        self._btn_start.setEnabled(True)
        self._btn_cancel.setEnabled(False)
        self._btn_close.setEnabled(True)
        self._btn_import.setEnabled(False)

    def is_running(self):
        return self._runner is not None and self._runner.is_alive()
Beispiel #46
0
class InfoBar(object):
    """Adds a info bar to the bottom of the main window"""

    # Should be stored and restored from uistate.ini
    infoSettings = {
        "showCPU": True,
        "showMemory": True,
        "showDisk": True,
        "showNetwork": True
    }

    def __init__(self):

        global gEnableResourceMonitoring
        self.enableResourceMonitoring = gEnableResourceMonitoring
        self.bar = hiero.ui.mainWindow().statusBar()

        self.updateMonitorIntervalMS = gUpdateIntervalMS  # The monitor update time in milliseconds.
        self.timer = QTimer()
        self.timer.setSingleShot(False)
        self.timer.timeout.connect(self.updateStatusBar)

        self.currentDiskIOBytes = psutil.disk_io_counters().read_bytes
        self.currentNetworkBytesReceived = psutil.net_io_counters().bytes_recv

        # This observes the current pid (the App process id) via psutil, and reports back
        if self.enableResourceMonitoring:
            self.processHelper = PSUtilProcessWrapper()

        # The frameServer instance
        self.frameServerInstance = nukestudio.frameServer

        # Initialise the Status Bar
        self.setupUI()

        # We haven't started monitoring at this point
        self.isMonitoring = False

        # Begin monitoring after a few secs to give frame server time to start up properly
        QTimer.singleShot(gInitialDelayMS, self.startMonitoring)

    def getLatestStyleSheet(self):
        styleFile = os.path.join(cwd, "style.stylesheet")
        with open(styleFile, "r") as fh:
            return fh.read()

    def hide(self):
        """Hide the info bar"""
        self.bar.setHidden(True)

    def show(self):
        """Show the info bar"""
        self.bar.setHidden(False)

    def rendersExistInQueue(self):
        """Return whether renders exist in the Frame server queue"""
        return len(self.frameServerInstance.renderQueue.requestsInProgress) > 0

    def _handleServerUnreachable(self):
        """This is called when the server becomes unreachable"""
        print "[WARNING]: Nuke Frame Server was not reachable."
        self.frameserverStatusLabel.setPixmap(QPixmap("icons:Offline.png"))
        self.restartServerButton.setHidden(False)

    def _setResourcesLabelColour(self, memRatio, cpuUsage):
        """Sets the Resources label to be red if the memory usage gets too high"""
        if memRatio > 0.9:
            color = QColor(Qt.red)
            alpha = 220
            values = "{r}, {g}, {b}, {a}".format(r=color.red(),
                                                 g=color.green(),
                                                 b=color.blue(),
                                                 a=alpha)
            #self.nukeResourcesLabel.setStyleSheet("QLabel { vertical-align: middle; font: 10pt; color: rgba("+values+"); }")
        else:
            #self.nukeResourcesLabel.setStyleSheet("QLabel { vertical-align: middle; font: 10pt; }")
            return

    def restartServerAsync(self):
        self.frameServerInstance.stop()
        self.frameServerInstance.start()
        QTimer.singleShot(3000, self.startMonitoring)

    def restartServer(self):
        """Called to restart the Nuke Frame Server, done asynchronously"""
        self.stopMonitoring()
        self.restartServerButton.setHidden(True)
        #self.nukeResourcesLabel.setText("Re-Starting Frame Server...")
        QTimer.singleShot(150, self.restartServerAsync)

    # Displays the resources label, based on settings.
    def buildResourcesLabel(self):
        if self.isMonitoring:
            print "Building resources Label..."

    def updateResourcesStatusLabel(self):
        """Updates the Memory Label String"""

        if self.enableResourceMonitoring:
            if self.memoryString.isVisible():
                self.updateMemoryLayout()

            if self.cpuString.isVisible():
                self.updateCPULayout()

            if self.diskString.isVisible():
                self.updateDiskLayout()

            if self.networkString.isVisible():
                self.updateNetworkLayout()

            #totalSystemMemoryGB = self.processHelper.totalSystemMemory()
            #memRatio = currentMemUsageGB / totalSystemMemoryGB
            # This little test makes the label red if the memory usage exceeds 90% of the maximum allowed
            #self._setResourcesLabelColour( memRatio, currentCPUUsageAsPercentatge )

    def updateMemoryLayout(self):
        currentMemUsageGB = self.processHelper.nukeMemoryUsageInGB()
        currentMemUsageAsPercentage = self.processHelper.nukeMemoryUsageAsPercentage(
        )
        self.memoryString.setText(
            "%.2f GB (%.1f%%)" %
            (currentMemUsageGB, currentMemUsageAsPercentage))

    def updateCPULayout(self):
        currentCPUUsageAsPercentatge = self.processHelper.nukeCPUUsageAsPercentage(
        )
        self.cpuString.setText("%.1f%%" % currentCPUUsageAsPercentatge)

    def updateDiskLayout(self):
        diskMBPerSec = self._diskMBPerSec()
        self.diskString.setText("%.2f MB/s" % diskMBPerSec)

    def updateNetworkLayout(self):
        networkMBPerSec = self._networkMBPerSec()
        self.networkString.setText("%.2f MB/s" % networkMBPerSec)

    def _diskMBPerSec(self):
        """Returns Total Disk Read+Write speed in MB/s"""
        oldBytes = self.currentDiskIOBytes
        DISKS = psutil.disk_io_counters(perdisk=True)
        readWriteBytes = [(DISKS[disk].read_bytes, DISKS[disk].write_bytes)
                          for disk in DISKS.keys()]
        newBytes = sum([sum(x) for x in zip(*readWriteBytes)])
        bytesDiff = newBytes - oldBytes
        self.currentDiskIOBytes = newBytes
        bytesPerSecond = (newBytes -
                          oldBytes) / (self.updateMonitorIntervalMS / 1000)
        MBPerSecond = bytesToMB(bytesPerSecond)
        return MBPerSecond

    def _networkMBPerSec(self):
        """Returns Total Network Read+Write speed in MB/s"""
        oldBytes = self.currentNetworkBytesReceived

        NET = psutil.net_io_counters(pernic=True)
        readWriteBytes = [(NET[adapter].bytes_recv, NET[adapter].bytes_sent)
                          for adapter in NET.keys()]
        newBytes = sum([sum(x) for x in zip(*readWriteBytes)])
        bytesDiff = newBytes - oldBytes
        self.currentNetworkBytesReceived = newBytes
        bytesPerSecond = (newBytes -
                          oldBytes) / (self.updateMonitorIntervalMS / 1000)
        MBPerSecond = bytesToMB(bytesPerSecond)
        return MBPerSecond

    def setupUI(self):
        """Initialise the UI"""

        self.bar.setStyleSheet(self.getLatestStyleSheet())
        #self.bar.setFixedHeight(30)
        self.frameserverStatusLabel = QLabel("")

        # Resources
        self.cpuIconPath = os.path.join(gIconPath, "cpu.png")
        self.memoryIconPath = os.path.join(gIconPath, "memory.png")
        self.diskReadIconPath = os.path.join(gIconPath, "disk_read.png")
        self.networkReadIconPath = os.path.join(gIconPath, "net_read.png")

        # MEMORY SECTION
        self.memoryImageButton = QPushButton(
            QPixmap(
                (self.memoryIconPath)).scaledToHeight(20,
                                                      Qt.SmoothTransformation),
            "")
        self.memoryImageButton.setObjectName("show_button_memory")
        self.memoryImageButton.setToolTip(
            "Click to toggle monitoring of 'Real Memory' usage")
        self.memoryString = QLabel("MEMORY (GB)")
        self.memoryString.setToolTip(
            "'Real Memory' usage of this Nuke Session")
        self.memoryImageButton.clicked.connect(
            lambda: self.show_button_clicked(self.memoryString))

        # CPU SECTION
        self.cpuImageButton = QPushButton(
            QPixmap(
                (self.cpuIconPath)).scaledToHeight(20,
                                                   Qt.SmoothTransformation),
            "")
        self.cpuImageButton.setObjectName("show_button_cpu")
        self.cpuImageButton.setToolTip(
            "Click to toggle monitoring of CPU usage of this Nuke Session")
        self.cpuString = QLabel("CPU (%)")
        self.cpuString.setToolTip("CPU usage of this Nuke Session")
        self.cpuImageButton.clicked.connect(
            lambda: self.show_button_clicked(self.cpuString))

        # DISK SECTION
        self.diskImageButton = QPushButton(
            QPixmap((self.diskReadIconPath)).scaledToHeight(
                20, Qt.SmoothTransformation), "")
        self.diskImageButton.setObjectName("show_button_disk")
        self.diskImageButton.setToolTip(
            "Click to toggle monitoring of Disk Read+Write usage for this machine"
        )
        self.diskString = QLabel("DISK (MB/s)")
        self.diskImageButton.clicked.connect(
            lambda: self.show_button_clicked(self.diskString))
        self.diskString.setToolTip("Disk Read+Write usage for this machine")

        # NETWORK SECTION
        self.networkImageButton = QPushButton(
            QPixmap((self.networkReadIconPath)).scaledToHeight(
                20, Qt.SmoothTransformation), "")
        self.networkImageButton.setObjectName("show_button_network")
        self.networkImageButton.setToolTip(
            "Click to toggle monitoring of Network Read+Write traffic")
        self.networkString = QLabel("NETWORK (MB/s)")
        self.networkString.setToolTip(
            "Total Network Read+Write traffic for this machine")
        self.networkImageButton.clicked.connect(
            lambda: self.show_button_clicked(self.networkString))

        # Settings Button - Displays what options should be shown in the Status Bar
        self.settingsButton = QPushButton()
        self.settingsButton.setIcon(QIcon("icons:Settings.png"))
        self.settingsButton.clicked.connect(self.showSettings)

        # Build the layout based on Preferences
        #self.cpuWidget.setVisible(self.infoSettings['showCPU'])
        #self.memoryWidget.setVisible(self.infoSettings['showMemory'])
        #self.diskWidget.setVisible(self.infoSettings['showDisk'])
        #self.networkWidget.setVisible(self.infoSettings['showNetwork'])

        self.restartServerButton = QPushButton(
            QPixmap("icons:TransformRotateRight.png").scaledToHeight(
                20, Qt.SmoothTransformation), "")
        self.restartServerButton.setFixedHeight(16)
        self.restartServerButton.clicked.connect(self.restartServer)
        self.restartServerButton.setHidden(True)
        self.restartServerButton.setFlat(True)
        self.restartServerButton.setToolTip(
            "Click here to restart the Nuke Frameserver")
        self.frameServerIsRendering = False

        self.spinnerMovie = QMovie("icons:RenderingSpinner.gif")
        self.spinnerMovie.start()

        self.bar.addPermanentWidget(self.cpuImageButton)
        self.bar.addPermanentWidget(self.cpuString)
        self.bar.addPermanentWidget(self.memoryImageButton)
        self.bar.addPermanentWidget(self.memoryString)
        self.bar.addPermanentWidget(self.diskImageButton)
        self.bar.addPermanentWidget(self.diskString)
        self.bar.addPermanentWidget(self.networkImageButton)
        self.bar.addPermanentWidget(self.networkString)
        self.bar.addPermanentWidget(self.frameserverStatusLabel)
        self.bar.addPermanentWidget(self.restartServerButton)
        self.bar.addPermanentWidget(self.settingsButton)

    def show_button_clicked(self, sender):
        sender.setVisible(not sender.isVisible())

    def _updateUIForServerIsRunning(self):
        """Updates the UI for when the server is reachable"""
        #self.frameserverStatusLabel.setToolTip("Nuke Frame Server is reachable")
        self.getFrameServerWorkers()
        self.frameserverStatusLabel.setPixmap(QPixmap("icons:OK.png"))
        self.restartServerButton.setHidden(True)

    def showSettings(self):
        dialog = SettingsDialog()
        dialog.show()

    # Returns a nicely formatted list of Frame Server workers
    def getFrameServerWorkers(self):
        statusString = str(self.frameServerInstance.getStatus(1000))
        workers = re.findall("workerStatus \{.*?\}", statusString)

        if len(workers) == 0:
            self.frameserverStatusLabel.setToolTip(
                "Unable to determine number of frame server workers.")
            return

        prettyWorkersString = "Frame Server Status (%i workers):\n" % len(
            workers) + "\n".join(workers)
        self.frameserverStatusLabel.setToolTip(prettyWorkersString)

    def updateStatusBar(self):
        """Updates the Status bar widgets depending on whether the frameServer is reachable"""

        #print "Status: ", str(self.frameServerInstance.getStatus(10))

        # DEBUG - Stylesheet Changes can be applied here and seen live
        #self.bar.setStyleSheet( self.getLatestStyleSheet() )

        try:
            isRunning = self.frameServerInstance.isRunning(0.25)

            if isRunning and not self.rendersExistInQueue():
                self._updateUIForServerIsRunning()
                self.frameServerIsRendering = False

            elif isRunning and self.rendersExistInQueue():
                if self.frameServerIsRendering == False:
                    self.frameServerIsRendering = True
                    self.frameserverStatusLabel.setPixmap(None)
                    self.frameserverStatusLabel.setMovie(self.spinnerMovie)

            else:
                self._handleServerUnreachable()

            self.updateResourcesStatusLabel()

        except:
            self._handleServerUnreachable()
            self.updateResourcesStatusLabel()

    def startMonitoring(self):
        """This timer fires every X milliseconds to update the status."""
        self.timer.start(self.updateMonitorIntervalMS)
        self.isMonitoring = True

    def stopMonitoring(self):
        """Stops the monitoring process"""
        self.timer.stop()
        self.isMonitoring = False
Beispiel #47
0
    class MainWindow(QWidget):
        def __init__(self, grid, U):
            assert isinstance(U, Communicable)
            super(MainWindow, self).__init__()

            U = U.data

            layout = QVBoxLayout()

            plotBox = QHBoxLayout()
            plot = GlumpyPatchWidget(self, grid, vmin=np.min(U), vmax=np.max(U), bounding_box=bounding_box, codim=codim)
            bar = ColorBarWidget(self, vmin=np.min(U), vmax=np.max(U))
            plotBox.addWidget(plot)
            plotBox.addWidget(bar)
            layout.addLayout(plotBox)

            if len(U) == 1:
                plot.set(U.ravel())
            else:
                plot.set(U[0])

                hlayout = QHBoxLayout()

                self.slider = QSlider(Qt.Horizontal)
                self.slider.setMinimum(0)
                self.slider.setMaximum(len(U) - 1)
                self.slider.setTickPosition(QSlider.TicksBelow)
                hlayout.addWidget(self.slider)

                lcd = QLCDNumber(m.ceil(m.log10(len(U))))
                lcd.setDecMode()
                lcd.setSegmentStyle(QLCDNumber.Flat)
                hlayout.addWidget(lcd)

                layout.addLayout(hlayout)

                hlayout = QHBoxLayout()

                toolbar = QToolBar()
                self.a_play = QAction(self.style().standardIcon(QStyle.SP_MediaPlay), 'Play', self)
                self.a_play.setCheckable(True)
                self.a_rewind = QAction(self.style().standardIcon(QStyle.SP_MediaSeekBackward), 'Rewind', self)
                self.a_toend = QAction(self.style().standardIcon(QStyle.SP_MediaSeekForward), 'End', self)
                self.a_step_backward = QAction(self.style().standardIcon(QStyle.SP_MediaSkipBackward), 'Step Back', self)
                self.a_step_forward = QAction(self.style().standardIcon(QStyle.SP_MediaSkipForward), 'Step', self)
                self.a_loop = QAction(self.style().standardIcon(QStyle.SP_BrowserReload), 'Loop', self)
                self.a_loop.setCheckable(True)
                toolbar.addAction(self.a_play)
                toolbar.addAction(self.a_rewind)
                toolbar.addAction(self.a_toend)
                toolbar.addAction(self.a_step_backward)
                toolbar.addAction(self.a_step_forward)
                toolbar.addAction(self.a_loop)
                hlayout.addWidget(toolbar)

                self.speed = QSlider(Qt.Horizontal)
                self.speed.setMinimum(0)
                self.speed.setMaximum(100)
                hlayout.addWidget(QLabel('Speed:'))
                hlayout.addWidget(self.speed)

                layout.addLayout(hlayout)

                self.timer = QTimer()
                self.timer.timeout.connect(self.update_solution)

                self.slider.valueChanged.connect(self.slider_changed)
                self.slider.valueChanged.connect(lcd.display)
                self.speed.valueChanged.connect(self.speed_changed)
                self.a_play.toggled.connect(self.toggle_play)
                self.a_rewind.triggered.connect(self.rewind)
                self.a_toend.triggered.connect(self.to_end)
                self.a_step_forward.triggered.connect(self.step_forward)
                self.a_step_backward.triggered.connect(self.step_backward)

                self.speed.setValue(50)

            self.setLayout(layout)
            self.plot = plot
            self.U = U

        def slider_changed(self, ind):
            self.plot.set(self.U[ind])

        def speed_changed(self, val):
            self.timer.setInterval(val * 20)

        def update_solution(self):
            ind = self.slider.value() + 1
            if ind >= len(self.U):
                if self.a_loop.isChecked():
                    ind = 0
                else:
                    self.a_play.setChecked(False)
                    return
            self.slider.setValue(ind)

        def toggle_play(self, checked):
            if checked:
                if self.slider.value() + 1 == len(self.U):
                    self.slider.setValue(0)
                self.timer.start()
            else:
                self.timer.stop()

        def rewind(self):
            self.slider.setValue(0)

        def to_end(self):
            self.a_play.setChecked(False)
            self.slider.setValue(len(self.U) - 1)

        def step_forward(self):
            self.a_play.setChecked(False)
            ind = self.slider.value() + 1
            if ind == len(self.U) and self.a_loop.isChecked():
                ind = 0
            if ind < len(self.U):
                self.slider.setValue(ind)

        def step_backward(self):
            self.a_play.setChecked(False)
            ind = self.slider.value() - 1
            if ind == -1 and self.a_loop.isChecked():
                ind = len(self.U) - 1
            if ind >= 0:
                self.slider.setValue(ind)
Beispiel #48
0
class MarqueeApp(QDialog):
    """
    Marquee App

    Shows scrolling texts on the QlockToo. You can modify the animation's
    direction, speed and style.
    """
    def __init__(self, device=None, simulator=None):
        super(MarqueeApp, self).__init__()
        self.ui = Ui()
        self.ui.setupUi(self)

        self.device = device
        self.simulator = simulator

        self.ui.text.cursorPositionChanged.connect(self.cursorPositionChanged)

        # create the update timer
        self.timer = QTimer()
        self.timer.timeout.connect(self.move)

        # marquee
        self.marquee = MarqueeMatrix(self.font)
        self.startposition = -self.simulator.columns
        self.speed = 50  # this is the initial dial position

        # init device with a black screen
        self.simulator.matrix = [[0] * 11] * 10
        self.simulator.corners = [0] * 4

    def cursorPositionChanged(self, old, new):
        """
        Shows the chars around the current cursor position on the device.
        """
        text = self.ui.text.text()
        self.marquee.setText(text)
        if len(text) > 0:
            self.simulator.matrix = self.marquee.regionFromLetter(new)

    def _frequency(self, speed):
        """
        Calculates the step frequency for the marquee from a given speed

        0 <= speed <= 100
        200 >= frequency >= 30
        """
        return 200 - 170 * abs(speed / 100.0)

    @Slot()
    def on_play_stateChanged(self):
        if self.ui.play.isChecked():
            self.x = self.startposition
            self.timer.start(self._frequency(self.speed))
        else:
            self.timer.stop()

    @Slot()
    def on_speed_valueChanged(self):
        """
        Gets called from the speed dial and sets the new speed.
        """
        self.speed = self.ui.speed.value()
        if self.ui.play.isChecked():
            self.timer.start(self._frequency(self.speed))

    def move(self):
        """
        Moves the marquee one step and shows in on the device.
        """
        # move forwards
        if self.speed >= 0:
            self.x += 1
            if self.x > self.marquee.width:
                self.x = self.startposition
        else:
            # move backwards
            self.x -= 1
            if self.x < self.startposition:
                self.x = self.marquee.width
        self.simulator.matrix = self.marquee.regionFromPosition(self.x)
Beispiel #49
0
class SpectrometerWindow(QMainWindow):
    def __init__(self, parent=None):
        # Set up everything
        QMainWindow.__init__(self, parent)
        self.setWindowTitle("USB4000 Control")

        self.make_main_frame()

        self.spectra_timer = QTimer()
        self.spectra_timer.timeout.connect(self.update_spectrum)

        self.temp_timer = QTimer()
        self.temp_timer.timeout.connect(self.update_temp)

        self.worker = Usb4000Thread()
        self.wavelength_mapping = self.worker.dev.get_wavelength_mapping()

        self.curves = []         
        self.persistence_sb.valueChanged.connect(self.change_persistence)
        self.change_persistence()
        
        self.background = 1
        self.bg_min = 0

        self.use_background = False

        self.abs645 = pg.InfiniteLine(angle=90, movable=False)
        self.abs663 = pg.InfiniteLine(angle=90, movable=False)

        self.plot.addItem(self.abs645, ignoreBounds=True)
        self.plot.addItem(self.abs663, ignoreBounds=True)
        
        self.abs645.setPos(645)
        self.abs663.setPos(663)
        
        self.conc_deque = deque(maxlen=20)

    def make_main_frame(self):
        self.main_frame = QWidget()
        win = pg.GraphicsWindow()
        self.crosshair_lb = pg.LabelItem(justify='right')
        win.addItem(self.crosshair_lb) 
        self.plot = win.addPlot(row=1, col=0) 
        self.plot.showGrid(x=True, y=True)
        self.right_panel = QGroupBox("Spectrometer Settings")
        
        hbox = QHBoxLayout()
        for w in [win, self.right_panel]:
            hbox.addWidget(w)

        form = QFormLayout()
        
        self.integration_sb = pg.SpinBox(value=0.001, bounds=(10*1e-6, 6553500*1e-6), suffix='s', siPrefix=True, \
                dec=True, step=1)
        self.integration_sb.valueChanged.connect(self.change_integration_time)

        self.persistence_sb = QtGui.QSpinBox()
        self.persistence_sb.setValue(7)
        self.persistence_sb.setRange(1,10)
        self.persistence_sb.valueChanged.connect(self.change_persistence)

        self.take_background_btn = QtGui.QPushButton('Take background')
        self.take_background_btn.clicked.connect(self.on_take_background)
        self.conc_lb = pg.ValueLabel()

        self.spec_temp_lb = pg.ValueLabel()

        self.use_background_cb = QtGui.QCheckBox("enabled")
        self.use_background_cb.stateChanged.connect(self.on_use_background)

        form.addRow("Integration time", self.integration_sb)
        form.addRow("Persistence",  self.persistence_sb)
        
        form.addRow("Background", self.take_background_btn)
        form.addRow("Use background", self.use_background_cb)
        self.right_panel.setLayout(form)
        self.main_frame.setLayout(hbox)
        self.setCentralWidget(self.main_frame)

    def on_use_background(self):
        if self.use_background_cb.isChecked():
            self.spectra_timer.stop()
            self.spectra_timer.start(500)
            self.persistence_sb.setValue(1)
            self.change_persistence()
            self.use_background = True
        else:
            self.spectra_timer.stop()
            self.spectra_timer.start(25)
            self.use_background = False

    def process_spectrum(self, data):
        if self.use_background:
            res =  np.log10(self.background) - np.log10(np.array(data, dtype='float'))
            self.conc_deque.append((res[1455]*20.2 + res[1539]*8.02)*0.2)
            self.crosshair_lb.setText("<span style='font-size: 12pt'>Abs645=%0.3f, <span style='color: red'>Abs663=%0.3f</span>, <span style='color: green'>Conc=%0.3f</span>" % (res[1455],res[1539], np.mean(np.array(self.conc_deque)))) 
            return res
        else:
            return np.array(data, dtype='float')
    
    def update_spectrum(self):
        self.data_stack.append(self.worker.get_spectrum())

        for i in xrange(len(self.data_stack)):
            d = self.data_stack[i]   
            if d != None: 
                log.debug('plotting curve %d', i)
                if i == len(self.data_stack)-1:
                    self.curves[i].setPen(color=(0, 255, 0, 255))
                else:
                    self.curves[i].setPen(color=(0, 255, 0, 50))
                self.curves[i].setData(self.wavelength_mapping, self.process_spectrum(d))

    def update_temp(self):
        res = self.worker.get_temp()

        if res != None:
            self.spec_temp_lb.setText("value")

    def change_integration_time(self):
        self.worker.set_integration_time(int(self.integration_sb.value()*1e6))

    def change_persistence(self):
        self.spectra_timer.stop()
        log.info('persistence changed')
        # remove all curves
        for i in self.curves: self.plot.removeItem(i)

        self.curves = []
        # add new curves
        val = self.persistence_sb.value()

        for i in xrange(val): 
            log.info('added %d item', i)
            self.curves.append(self.plot.plot())

        self.data_stack = deque(maxlen=val)
        log.info('maxlen is %d', val)
        self.spectra_timer.start(25)
    
    def on_take_background(self):
        self.spectra_timer.stop()
        self.data_stack.clear()

        bg = self.worker.get_spectrum()
        self.bg_min = np.amin(bg)
        self.background = np.array(bg, dtype='float')
        self.spectra_timer.start(25)

    def close(self):
        self.worker.join()

    def show(self):
        self.temp_timer.start(1000)
        self.spectra_timer.start(25)
        self.worker.start()
        super(QMainWindow, self).show()
        
    def closeEvent(self, event):
        super(QMainWindow, self).closeEvent(event)
class Browser(object):

    def __init__(self, callback, logger, options=None):
        self.logger = logger
        options = options or dict()

        self._request_ops = {'head': QNetworkAccessManager.HeadOperation,
                             'get': QNetworkAccessManager.GetOperation,
                             'put': QNetworkAccessManager.PutOperation,
                             'post': QNetworkAccessManager.PostOperation,
                             'delete': QNetworkAccessManager.DeleteOperation}

        self._timeout = int(options.pop('timeout', 30)) * 1000

        max_request_retries = options.pop('max_request_retries', 3)
        self._network_manager = SmartNetworkAccessManager(logger,
                                                          max_request_retries)
        self._web_page = CraftyWebPage()
        self._web_page.setNetworkAccessManager(self._network_manager)

        self._web_view = QWebView()
        self._web_view.setPage(self._web_page)

        # connect the loadFinished signal to a method defined by us.
        # loadFinished is the signal which is triggered when a page is loaded
        self._web_view.loadFinished.connect(self._load_finished)

        settings = self._web_view.settings()
        settings.setAttribute(QWebSettings.AutoLoadImages,
                              options.pop('images', False))
        settings.setAttribute(QWebSettings.JavascriptEnabled,
                              options.pop('javascript', False))
        settings.setAttribute(QWebSettings.JavascriptCanOpenWindows,
                              options.pop('popups', False))
        settings.setAttribute(QWebSettings.PrivateBrowsingEnabled,
                              options.pop('private_browsing', False))
        settings.setAttribute(QWebSettings.JavaEnabled, False)
        settings.setAttribute(QWebSettings.PluginsEnabled, False)
        settings.setAttribute(QWebSettings.DnsPrefetchEnabled, True)

        # store the callback function which will be called when a request is
        # finished
        self._result_callback = callback
        self._is_task_finished = False
        self._destroyed_status = dict()

    def _prepare_request(self, url, headers):
        # create an empty request
        request = QNetworkRequest()
        # assign a url to it
        request.setUrl(QUrl(url))

        # add some custom headers to the request
        for (header_name, header_value) in headers.items():
            request.setRawHeader(header_name, QByteArray(header_value))

        return request

    def _urlencode_request_data(self, raw_data):
        # the data which we want to send to the server must be urlencoded
        request_data = QUrl()
        for (name, value) in raw_data.items():
            request_data.addQueryItem(name, unicode(value))

        return request_data.encodedQuery()

    def _load_finished(self, ok):
        """
        Called when the page is fully loaded. It will get the html file of
        the loaded page and call the callback function with that result.
        """
        if self._is_task_finished:
            # in case loadFinished fires more than once and we already
            # reported back with a result, don't do that again
            self.logger.info('loadFinished emitted, but task was already '
                             'finished.')
            return

        pending_requests = self._network_manager.active_requests

        if ok == 'timed_out':
            self.logger.info('loadFinished emitted, request timed out.')
            self._network_manager.errors.append('Request timed out.')
            # to avoid treating the request by the driver as successful
            ok = False
        elif len(pending_requests) > 0:
            self.logger.info("loadFinished emitted, waiting for requests:"
                             " {0}".format(pending_requests))
            loaded = partial(lambda x: self._load_finished(x), ok)
            QTimer.singleShot(1000, loaded)
            return

        self.logger.info('loadFinshed emitted, returning result.')
        frame = self._web_view.page().mainFrame()
        url = smart_str(frame.url().toString())
        html = frame.toHtml()

        result = {'html': html,
                  'url': url,
                  'successful': ok}

        if self._network_manager.errors:
            result['errors'] = self._network_manager.errors

        self._finish_task(result)

    def _start_task(self):
        self._is_task_finished = False
        # abusing the ok param of loadFinished
        timed_out = lambda: self._load_finished('timed_out')
        self._timeout_timer = QTimer()
        self._timeout_timer.timeout.connect(timed_out)
        self._timeout_timer.start(self._timeout)

    def _finish_task(self, result):
        self._is_task_finished = True
        self._timeout_timer.stop()
        # calling the callback function which we passed upon instantiation to
        # report the results there
        self._result_callback(result)

    def make(self, method, url, headers, raw_data=None):
        request = self._prepare_request(url, headers)
        operation = self._request_ops[method.lower()]
        request_data = self._urlencode_request_data(raw_data or dict())
        self._start_task()
        self._web_view.load(request, operation, request_data)

    def _find_element(self, selector):
        main_frame = self._web_page.mainFrame()
        element = main_frame.findFirstElement(selector)

        if element.isNull():
            raise ElementNotFound(selector)

        return element

    def fill_input(self, selector, value):
        js_fill_input = """
            this.setAttribute("value", "{0}");
            this.value = "{0}";
        """.format(value)

        element = self._find_element(selector)
        element.evaluateJavaScript(js_fill_input)

    def click(self, selector):
        element = self._find_element(selector)
        offset = element.geometry()
        js_click = """
            function mouse_click(element) {{
                var event = document.createEvent('MouseEvents');
                var offsetX = {0} + 2;  //add 2 pixels otherwise it would
                var offsetY = {1} - 2;  //seem like we clicked on the margin
                event.initMouseEvent(
                    'click',                    //event type
                    true,                       //canBubble
                    true,                       //cancelable
                    document.defaultView,       //view
                    1,                          //detail
                    (window.screenX + offsetX), //screenX - The coords within
                    (window.screenY + offsetY), //screenY - the entire page
                    offsetX,                    //clientX - The coords within
                    offsetY,                    //clientY - the viewport
                    false,                      //ctrlKey
                    false,                      //altKey
                    false,                      //shiftKey
                    false,                      //metaKey
                    0,                          //0=left, 1=middle, 2=right
                    element                     //relatedTarget
                );
                element.dispatchEvent(event);   //Fire the event
            }}
            mouse_click(this);""".format(offset.left(), offset.top())

        self._start_task()
        element.evaluateJavaScript(js_click)

    def _destroyed(self, component):
        self._destroyed_status[component] = True
        if all(self._destroyed_status.values()):
            self._shutdown_callback()

    def shutdown(self, callback):
        self._shutdown_callback = callback
        self._web_view.stop()
        self._web_view.close()
        # will immediately stop any running javascript code
        self._web_view.settings().setAttribute(QWebSettings.JavascriptEnabled,
                                               False)
        # if any requests were started by javascript after loadFinished was
        # emitted, and before we stopped javascript execution, cancel them
        self._network_manager.abort_requests()

        self._destroyed_status['web_page'] = False
        self._web_page.destroyed.connect(lambda: self._destroyed('web_page'))
        self._web_page.deleteLater()

        self._destroyed_status['web_view'] = False
        self._web_view.destroyed.connect(lambda: self._destroyed('web_view'))
        self._web_view.deleteLater()

        self._destroyed_status['network_manager'] = False
        destroyer = lambda: self._destroyed('network_manager')
        self._network_manager.destroyed.connect(destroyer)
        self._network_manager.deleteLater()
class DisplayController(QObject):
    """ Controller class for the display app

    Acts as a coordinator between ClimateControlModel and DisplayView
    """
    def __init__(self, parent=None):
        super(DisplayController, self).__init__(parent)
        _, external_version = get_app_version()
        self.__display_app = DisplayView(external_version)
        self.__display_app.init_login_view(UserList().user_names)
        self.__display_app.register_login_callback(self.__login_btn_clicked)
        self.__display_app.close_signal.connect(self.__close)

        self.__model = None
        self.__refresh_graph_timer = QTimer(self)
        self.__refresh_graph_timer.timeout.connect(self.__speculate_data)
        self.temperature_data = collections.OrderedDict()
        self.humidity_data = collections.OrderedDict()
        self.temperature = None
        self.humidity = None
        self.__display_view_initialized = False

    @Slot(str, str)
    def __login_btn_clicked(self, user_name, password):
        """ Slot for the login button

         Tries to create model with the provided user_name and password

        :param str user_name: flow user name
        :param str password: flow user password
        """
        # clear any old status
        self.__display_app.login_view_show_error(None)

        LOGGER.debug("Trying to login with username = {}".format(user_name))

        try:
            # try to initialize model
            self.__model = ClimateControlModel()
            self.__model.connection_status.connect(self.__connection_status_result)
            try:
                self.__model.initialize(user_name, password)
            except LoginFailed:
                self.__display_app.login_view_show_error(UIStrings.LOGIN_FAILED)
            except ControllerNotFound:
                self.__display_app.login_view_show_error(UIStrings.CONTROLLER_DEVICE_NOT_FOUND)
            else:
                # Save username if no exception comes
                UserList().add_name(user_name)
                self.__model.setting_received.connect(self.__get_setting_response)
                # define slot for receiving events from Controller
                self.__model.measurement_changed.connect(self.__measurement_changed)
                self.__model.device_status_changed.connect(self.__device_status_changed)
                self.__model.relay_status_changed.connect(self.__relay_status_changed)
                self.__model.controller_status.connect(self.__controller_status)
                self.__model.latency_changed.connect(self.__latency_changed)
                self.__model.get_settings()
        except FlowLibraryError:
            self.__display_app.login_view_show_error(UIStrings.FLOW_LIBRARY_ERROR)

    @Slot(dict)
    def __get_setting_response(self, result):
        """ Slot for the get setting response from ClimateControlModel

        Initializes display view with received setting or shows error
        :param dict result: dictionary of the setting to be shown on configuration tab
        """
        if not self.__display_view_initialized:
            self.__display_app.close_login_view()
            self.__display_app.init_display_view(result["setting"])
            self.__display_view_initialized = True
            if result["error"]:
                self.__display_app.update_status_bar(UIStrings.SETTING_NOT_RECEIVED)
                LOGGER.error(result["error"])

        else:
            self.__display_app.update_settings(result["setting"])

    @Slot(dict)
    def __connection_status_result(self, connection_status):
        """Slot function for connection status result

        :param dict connection_status: dictionary containing network and internet status
        """
        status = UIStrings.OK
        color = "green"
        if not connection_status["network"]:
            color = "red"
            status = UIStrings.NETWORK_DOWN
        elif not connection_status["internet"]:
            status = UIStrings.INTERNET_DOWN
            color = "red"
        self.__display_app.set_connection_status(status, color)

    @Slot()
    def __close(self):
        """ Slot function when view gets closed

        Tries to de-initialize the model and stop refresh graph timer if active
        """
        if self.__refresh_graph_timer.isActive():
            self.__refresh_graph_timer.stop()

        LOGGER.debug("closing model")
        if self.__model:
            self.__model.close()

    @Slot(MeasurementEvent)
    def __measurement_changed(self, event):
        """ Slot function which receives measurement event from model

        :param MeasurementEvent event: Measurement Event object received from model
        """
        if self.__refresh_graph_timer.isActive():
            self.__refresh_graph_timer.stop()
        self.temperature = event.temperature
        self.humidity = event.humidity
        self.__update_data(self.temperature, self.humidity)
        self.__display_app.plot_graph(self.temperature_data, self.humidity_data)
        self.__refresh_graph_timer.start(REFRESH_GRAPH_TIMEOUT)

    @Slot()
    def __speculate_data(self):
        """ This function is a slot for refresh_graph timeout event

        It updates measurement data with last received measurement values
        """
        self.__update_data(self.temperature, self.humidity)
        self.__display_app.plot_graph(self.temperature_data, self.humidity_data)
        self.__refresh_graph_timer.start(REFRESH_GRAPH_TIMEOUT)

    def __update_data(self, temperature, humidity):
        """  Updates measurement data
        :param float temperature: temperature value
        :param float humidity: humidity value
        """
        current_time = datetime.datetime.now()
        if not (self.temperature_data and self.humidity_data):
            # fill the dictionary with last GRAPH_WINDOW_SIZE seconds of data
            for i in range(GRAPH_WINDOW_SIZE, 0, -1):
                self.temperature_data[current_time - datetime.timedelta(seconds=i)] = 0
                self.humidity_data[current_time - datetime.timedelta(seconds=i)] = 0

        self.temperature_data[current_time] = temperature
        self.humidity_data[current_time] = humidity
        if current_time - self.temperature_data.keys()[0] > datetime.\
                timedelta(seconds=GRAPH_WINDOW_SIZE):
            del self.temperature_data[self.temperature_data.keys()[0]]
            del self.humidity_data[self.humidity_data.keys()[0]]

    @Slot(str)
    def __controller_status(self, status):
        """ Slot function which receives controller device status from model

        It configures refresh graph timer according to controller device status
        :param str status: controller device status
        """
        LOGGER.debug("Controller status received - {}".format(status))
        if self.__display_app:
            self.__display_app.\
                update_device_status(DeviceEnum.controller, status == "ONLINE")
            # Latency is not applicable if controller is OFFLINE
            if status == "OFFLINE":
                self.__display_app.update_latency(None)
                LOGGER.debug("Latency set to NA")

            if status == "ONLINE" and not self.__refresh_graph_timer.isActive():
                self.__refresh_graph_timer.start(REFRESH_GRAPH_TIMEOUT)
                LOGGER.debug("Refresh graph timer started")

            elif status == "OFFLINE" and self.__refresh_graph_timer.isActive():
                self.__refresh_graph_timer.stop()
                LOGGER.debug("Refresh graph timer stopped")

            elif status == "ONLINE" and self.__refresh_graph_timer.isActive():
                LOGGER.debug("Refresh graph timer is already active")

            elif status == "OFFLINE" and not self.__refresh_graph_timer.isActive():
                LOGGER.debug("Refresh graph timer is already inactive")

        else:
            LOGGER.debug("Controller status received before display window is initialized")

    @Slot(DeviceStatusEvent)
    def __device_status_changed(self, event):
        """ Slot function which receives sensor and actuator status from model

        :param DeviceStatusEvent event: DeviceStatusEvent object
        """
        if self.__display_app:
            self.__display_app.update_device_status(DeviceEnum.sensor, event.sensor_alive)
            self.__display_app.update_device_status(DeviceEnum.actuator, event.actuator_alive)

        else:
            LOGGER.debug("Device(sensor and actuator) status received before display window is "
                         "initialized")

    @Slot(RelayStatusEvent)
    def __relay_status_changed(self, event):
        """ Slot function which receives relay status event from model

        :param RelayStatusEvent event: RelayStatusEvent object
        """
        if self.__display_app:
            self.__display_app.update_relay_status(event)

        else:
            LOGGER.debug("Relay status received before display window is initialized")

    @Slot(float)
    def __latency_changed(self, latency):
        """ Slot function which receives latency from model

        :param float latency: latency value
        """
        if self.__display_app:
            self.__display_app.update_latency(latency)
Beispiel #52
0
    class PlotMainWindow(QWidget):
        """Base class for plot main windows."""

        def __init__(self, U, plot, length=1, title=None):
            super(PlotMainWindow, self).__init__()

            layout = QVBoxLayout()

            if title:
                title = QLabel('<b>' + title + '</b>')
                title.setAlignment(Qt.AlignHCenter)
                layout.addWidget(title)
            layout.addWidget(plot)

            plot.set(U, 0)

            if length > 1:
                hlayout = QHBoxLayout()

                self.slider = QSlider(Qt.Horizontal)
                self.slider.setMinimum(0)
                self.slider.setMaximum(length - 1)
                self.slider.setTickPosition(QSlider.TicksBelow)
                hlayout.addWidget(self.slider)

                lcd = QLCDNumber(m.ceil(m.log10(length)))
                lcd.setDecMode()
                lcd.setSegmentStyle(QLCDNumber.Flat)
                hlayout.addWidget(lcd)

                layout.addLayout(hlayout)

                hlayout = QHBoxLayout()

                toolbar = QToolBar()
                self.a_play = QAction(self.style().standardIcon(QStyle.SP_MediaPlay), 'Play', self)
                self.a_play.setCheckable(True)
                self.a_rewind = QAction(self.style().standardIcon(QStyle.SP_MediaSeekBackward), 'Rewind', self)
                self.a_toend = QAction(self.style().standardIcon(QStyle.SP_MediaSeekForward), 'End', self)
                self.a_step_backward = QAction(self.style().standardIcon(QStyle.SP_MediaSkipBackward),
                                               'Step Back', self)
                self.a_step_forward = QAction(self.style().standardIcon(QStyle.SP_MediaSkipForward), 'Step', self)
                self.a_loop = QAction(self.style().standardIcon(QStyle.SP_BrowserReload), 'Loop', self)
                self.a_loop.setCheckable(True)
                toolbar.addAction(self.a_play)
                toolbar.addAction(self.a_rewind)
                toolbar.addAction(self.a_toend)
                toolbar.addAction(self.a_step_backward)
                toolbar.addAction(self.a_step_forward)
                toolbar.addAction(self.a_loop)
                if hasattr(self, 'save'):
                    self.a_save = QAction(self.style().standardIcon(QStyle.SP_DialogSaveButton), 'Save', self)
                    toolbar.addAction(self.a_save)
                    self.a_save.triggered.connect(self.save)
                hlayout.addWidget(toolbar)

                self.speed = QSlider(Qt.Horizontal)
                self.speed.setMinimum(0)
                self.speed.setMaximum(100)
                hlayout.addWidget(QLabel('Speed:'))
                hlayout.addWidget(self.speed)

                layout.addLayout(hlayout)

                self.timer = QTimer()
                self.timer.timeout.connect(self.update_solution)

                self.slider.valueChanged.connect(self.slider_changed)
                self.slider.valueChanged.connect(lcd.display)
                self.speed.valueChanged.connect(self.speed_changed)
                self.a_play.toggled.connect(self.toggle_play)
                self.a_rewind.triggered.connect(self.rewind)
                self.a_toend.triggered.connect(self.to_end)
                self.a_step_forward.triggered.connect(self.step_forward)
                self.a_step_backward.triggered.connect(self.step_backward)

                self.speed.setValue(50)

            elif hasattr(self, 'save'):
                hlayout = QHBoxLayout()
                toolbar = QToolBar()
                self.a_save = QAction(self.style().standardIcon(QStyle.SP_DialogSaveButton), 'Save', self)
                toolbar.addAction(self.a_save)
                hlayout.addWidget(toolbar)
                layout.addLayout(hlayout)
                self.a_save.triggered.connect(self.save)

            self.setLayout(layout)
            self.plot = plot
            self.U = U
            self.length = length

        def slider_changed(self, ind):
            self.plot.set(self.U, ind)

        def speed_changed(self, val):
            self.timer.setInterval(val * 20)

        def update_solution(self):
            ind = self.slider.value() + 1
            if ind >= self.length:
                if self.a_loop.isChecked():
                    ind = 0
                else:
                    self.a_play.setChecked(False)
                    return
            self.slider.setValue(ind)

        def toggle_play(self, checked):
            if checked:
                if self.slider.value() + 1 == self.length:
                    self.slider.setValue(0)
                self.timer.start()
            else:
                self.timer.stop()

        def rewind(self):
            self.slider.setValue(0)

        def to_end(self):
            self.a_play.setChecked(False)
            self.slider.setValue(self.length - 1)

        def step_forward(self):
            self.a_play.setChecked(False)
            ind = self.slider.value() + 1
            if ind == self.length and self.a_loop.isChecked():
                ind = 0
            if ind < self.length:
                self.slider.setValue(ind)

        def step_backward(self):
            self.a_play.setChecked(False)
            ind = self.slider.value() - 1
            if ind == -1 and self.a_loop.isChecked():
                ind = self.length - 1
            if ind >= 0:
                self.slider.setValue(ind)
Beispiel #53
0
class Plot2DDataWidget(QWidget):
    def __init__(self,parent=None,measdata=[[1,3,2],[3,5,7]],header=["index","prime numbers"],SymbolSize=10,linecolor='y',pointcolor='b',title='Plot Window'):
        # This class derivates from a Qt MainWindow so we have to call
        # the class builder ".__init__()"
        #QMainWindow.__init__(self)
        QWidget.__init__(self)
        # "self" is now a Qt Mainwindow, then we load the user interface
        # generated with QtDesigner and call it self.ui
        self.ui = Plot2DDataWidget_Ui.Ui_Plot2DData()
        # Now we have to feed the GUI building method of this object (self.ui)
        # with a Qt Mainwindow, but the widgets will actually be built as children
        # of this object (self.ui)
        self.ui.setupUi(self)
        self.setWindowTitle(title)
        self.x_index=0
        self.y_index=0
        self.curve=self.ui.plot_area.plot(pen=linecolor)
        self.curve.setSymbolBrush(pointcolor)
        self.curve.setSymbol('o')
        self.curve.setSymbolSize(SymbolSize)
        
        self.parent=parent
        self.measdata=measdata
        self.header=header
        self.update_dropdown_boxes(header)
        
        self.update_plot_timer = QTimer()
        self.update_plot_timer.setSingleShot(True) #The timer would not wait for the completion of the task otherwise
        self.update_plot_timer.timeout.connect(self.autoupdate)

        if self.ui.auto_upd.isChecked():self.autoupdate()
            
    def update_timer_timeout(self,msec):
        self.update_plot_timer.setInterval(msec)
        
    def updateX(self,value):
        self.x_index=value
        #self.update_plot() #that was the bug
        #This created a loop because update_plot called check_connection
        #which called update_dropdown_boxes, which cleared the x_axis_box
        #which triggered a call to updateX
        
    def updateY(self,value):
        self.y_index=value
        #self.update_plot()
        
    def change_line_color(self,color=None):
        if color==None:color=QColorDialog.getColor()
        if color.isValid():
            self.curve.setPen(color)
            
    def change_point_color(self,color=None):
        if color==None:color=QColorDialog.getColor()
        if color.isValid():
            self.curve.setSymbolBrush(color)
            
    def change_symbol_size(self,value):
        self.curve.setSymbolSize(value)

    def check_connection(self,state=1):
        """check if the pointer to the Master dataset to display (self.measdata in Main.py) has changed"""
        if state and hasattr(self.parent,"measdata"):
            if self.parent.measdata!=self.measdata:self.measdata=self.parent.measdata
            if self.parent.current_header!=self.header:self.update_dropdown_boxes(self.parent.current_header)
            #print "Reestablishing connection"
    
    def autoupdate(self,state=1):
        if state:
            self.update_plot()
            self.update_plot_timer.start(self.ui.refresh_rate.value()*1000)#The value must be converted to milliseconds
        else:
            self.update_plot_timer.stop()
            
    def update_plot(self):
        """plot the data columns selected in the drop-down menu boxes"""
        if self.ui.autoconnect.isChecked():self.check_connection()
        if self.x_index!=-1 and self.y_index!=-1 and self.measdata[self.x_index]!=[] and self.measdata[self.y_index]!=[]:            
            self.curve.setData(self.measdata[self.x_index],self.measdata[self.y_index])
    
    def update_dropdown_boxes(self,header):
        """Update the drop-down boxes that select the content of the plot"""
        self.ui.x_axis_box.clear()
        self.ui.x_axis_box.addItems(header) 
        self.ui.y_axis_box.clear()
        self.ui.y_axis_box.addItems(header)
        self.header=header
class TonePlayer(QObject):

    changed = Signal()

    def getStateLabel(self):
        print "Getting"
        return self._label

    def setStateLabel(self, value):
        print "Setting", value
        self._label = value
        self.changed.emit()

    stateLabel = Property(str, getStateLabel, setStateLabel, notify=changed)

    def __init__(self, devices=None, filename=None, parent=None):
        QObject.__init__(self, parent)

        self.pullTimer = QTimer(self)
        self.buf = QByteArray()
        self.devices = devices
        self.device = QAudioDeviceInfo.defaultOutputDevice()
        self.generator = None
        self.audioOutput = None
        self.output = None
        self.fmt = QAudioFormat()
        self.pullMode = False
        self.dump = filename
        self._label = SUSPEND_LABEL

        self.initializeAudio()

    def initializeAudio(self):
        self.pullTimer.timeout.connect(self.pullTimerExpired)

        self.pullMode = True

        self.fmt.setFrequency(DATA_FREQUENCY_HZ)
        self.fmt.setChannels(1)
        self.fmt.setSampleSize(16)
        self.fmt.setCodec('audio/pcm')
        self.fmt.setByteOrder(QAudioFormat.LittleEndian)
        self.fmt.setSampleType(QAudioFormat.SignedInt)

        info = QAudioDeviceInfo(QAudioDeviceInfo.defaultOutputDevice())
        if not info.isFormatSupported(self.fmt):
            print 'Default format not supported - trying to use nearest'
            self.fmt = info.nearestFormat(self.fmt)

        self.generator = Generator(self.fmt, DURATION_SECONDS * 1000000,
                                   TONE_FREQUENCY_HZ, self, self.dump)

        self.createAudioOutput()

    def createAudioOutput(self):
        self.audioOutput = QAudioOutput(self.device, self.fmt, self)
        self.audioOutput.notify.connect(self.notified)
        self.audioOutput.stateChanged.connect(self.stateChanged)
        self.generator.start()
        self.audioOutput.start(self.generator)

    @Slot()
    def toggleSuspendResume(self):
        if self.audioOutput.state() == QAudio.SuspendedState:
            print 'Status: Suspended, resuming'
            self.audioOutput.resume()
            self.stateLabel = SUSPEND_LABEL
        elif self.audioOutput.state() == QAudio.ActiveState:
            print 'Status: Active, suspending'
            self.audioOutput.suspend()
            self.stateLabel = RESUME_LABEL
        elif self.audioOutput.state() == QAudio.StoppedState:
            print 'Status: Stopped, resuming'
            self.audioOutput.resume()
            self.stateLabel = SUSPEND_LABEL
        elif self.audioOutput.state() == QAudio.IdleState:
            print 'Status: Idle'

    playbackResumed = Signal()

    @Slot()
    def toggleMode(self):
        self.pullTimer.stop()
        self.audioOutput.stop()

        if self.pullMode:
            print "Enabling push mode"
            self.output = self.audioOutput.start()
            self.pullMode = False
            self.pullTimer.start(5)
        else:
            print "Enabling pull mode"
            self.pullMode = True
            self.audioOutput.start(self.generator)
        self.playbackResumed.emit()

    @Slot(int)
    def deviceChanged(self, index):
        print "Device changed: index:", index
        print "Selected device name: ", self.devices[index].deviceName()
        self.pullTimer.stop()
        self.generator.stop()
        self.audioOutput.stop()
        self.audioOutput.disconnect(self)
        self.device = self.devices[index]
        self.createAudioOutput()

    def stateChanged(self, state):
        print 'State changed: ', state

    def notified(self):
        print 'Bytes free %d, elapsed usecs %d, processed usecs %d' % (
            self.audioOutput.bytesFree(), self.audioOutput.elapsedUSecs(),
            self.audioOutput.processedUSecs())

    def pullTimerExpired(self):
        if self.audioOutput.state() != QAudio.StoppedState:
            chunks = self.audioOutput.bytesFree(
            ) / self.audioOutput.periodSize()
            while chunks:
                data = self.generator.read(self.audioOutput.periodSize())
                self.output.write(data)
                if len(data) != self.audioOutput.periodSize():
                    break
                chunks -= 1
Beispiel #55
0
class QtReactor(posixbase.PosixReactorBase):
    implements(IReactorFDSet)

    def __init__(self):
        self._reads = {}
        self._writes = {}
        self._notifiers = {}
        self._timer = QTimer()
        self._timer.setSingleShot(True)
        QObject.connect(self._timer, SIGNAL("timeout()"), self.iterate)

        if QCoreApplication.instance() is None:
            # Application Object has not been started yet
            self.qApp=QCoreApplication([])
            self._ownApp=True
        else:
            self.qApp = QCoreApplication.instance()
            self._ownApp=False
        self._blockApp = None
        posixbase.PosixReactorBase.__init__(self)


    def _add(self, xer, primary, type):
        """
Private method for adding a descriptor from the event loop.

It takes care of adding it if new or modifying it if already added
for another state (read -> read/write for example).
"""
        if xer not in primary:
            primary[xer] = TwistedSocketNotifier(None, self, xer, type)


    def addReader(self, reader):
        """
Add a FileDescriptor for notification of data available to read.
"""
        self._add(reader, self._reads, QSocketNotifier.Read)


    def addWriter(self, writer):
        """
Add a FileDescriptor for notification of data available to write.
"""
        self._add(writer, self._writes, QSocketNotifier.Write)


    def _remove(self, xer, primary):
        """
Private method for removing a descriptor from the event loop.

It does the inverse job of _add, and also add a check in case of the fd
has gone away.
"""
        if xer in primary:
            notifier = primary.pop(xer)
            notifier.shutdown()

        
    def removeReader(self, reader):
        """
Remove a Selectable for notification of data available to read.
"""
        self._remove(reader, self._reads)


    def removeWriter(self, writer):
        """
Remove a Selectable for notification of data available to write.
"""
        self._remove(writer, self._writes)


    def removeAll(self):
        """
Remove all selectables, and return a list of them.
"""
        rv = self._removeAll(self._reads, self._writes)
        return rv


    def getReaders(self):
        return self._reads.keys()


    def getWriters(self):
        return self._writes.keys()


    def callLater(self,howlong, *args, **kargs):
        rval = super(QtReactor,self).callLater(howlong, *args, **kargs)
        self.reactorInvocation()
        return rval


    def reactorInvocation(self):
        self._timer.stop()
        self._timer.setInterval(0)
        self._timer.start()
        

    def _iterate(self, delay=None, fromqt=False):
        """See twisted.internet.interfaces.IReactorCore.iterate.
"""
        self.runUntilCurrent()
        self.doIteration(delay, fromqt)

    iterate = _iterate

    def doIteration(self, delay=None, fromqt=False):
        'This method is called by a Qt timer or by network activity on a file descriptor'
        
        if not self.running and self._blockApp:
            self._blockApp.quit()
        self._timer.stop()
        delay = max(delay, 1)
        if not fromqt:
            self.qApp.processEvents(QEventLoop.AllEvents, delay * 1000)
        if self.timeout() is None:
            timeout = 0.1
        elif self.timeout() == 0:
            timeout = 0
        else:
            timeout = self.timeout()
        self._timer.setInterval(timeout * 1000)
        self._timer.start()


    def runReturn(self, installSignalHandlers=True):
        self.startRunning(installSignalHandlers=installSignalHandlers)
        self.reactorInvocation()


    def run(self, installSignalHandlers=True):
        if self._ownApp:
            self._blockApp = self.qApp
        else:
            self._blockApp = QEventLoop()
        self.runReturn()
        self._blockApp.exec_()
Beispiel #56
0
class MapWidget(QGraphicsGeoMap):

    coordQueryResult = Signal(QGeoCoordinate)

    def __init__(self, manager):
        QGraphicsGeoMap.__init__(self, manager)

        self.coordQueryState = False
        self.panActive = False
        self.kineticTimer = QTimer()
        self.lastCircle = None
        self.lastMoveTime = None
        self.kineticPanSpeed = None
        self.panDecellerate = True
        self.remainingPan = QPointF()

        self.mouseHistory = deque([MouseHistoryEntry() for i in range(5)])

        self.kineticTimer.timeout.connect(self.kineticTimerEvent)
        self.kineticTimer.setInterval(KINETIC_PANNING_RESOLUTION)

    def setMouseClickCoordQuery(self, state):
        self.coordQueryState = state

    def mousePressEvent(self, event):
        self.setFocus()

        if event.button() == Qt.LeftButton:
            if event.modifiers() & Qt.ControlModifier:
                pass
            else:
                if self.coordQueryState:
                    self.coordQueryResult.emit(self.screenPositionToCoordinate(event.lastPos()))
                    return

                self.panActive = True

                self.kineticTimer.stop()
                self.kineticPanSpeed = QPointF()
                self.lastMoveTime = QTime.currentTime()

        event.accept()

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            if self.panActive:
                self.panActive = False

                if not ENABLE_KINETIC_PANNING or self.lastMoveTime.msecsTo(QTime.currentTime()) > HOLD_TIME_THRESHOLD:
                    return

                self.kineticPanSpeed = QPointF()
                entriesConsidered = 0

                currentTime = QTime.currentTime()

                for entry in self.mouseHistory:
                    if not entry.time:
                        continue

                    if entry.time.msecsTo(currentTime) < HOLD_TIME_THRESHOLD:
                        self.kineticPanSpeed += entry.position
                        entriesConsidered += 1

                if entriesConsidered > 0:
                    self.kineticPanSpeed /= entriesConsidered

                self.lastMoveTime = currentTime

                self.kineticTimer.start()
                self.panDecellerate = True

        event.accept()

    def mouseMoveEvent(self, event):
        if event.modifiers() & Qt.ControlModifier:
            if self.lastCircle:
                self.lastCircle.setCenter(self.screenPositionToCoordinate(event.pos()))
        elif self.panActive:
            currentTime = QTime.currentTime()
            deltaTime = self.lastMoveTime.msecsTo(currentTime)

            delta = event.lastPos() - event.pos()

            if deltaTime > 0:
                self.kineticPanSpeed = delta / deltaTime

                self.mouseHistory.popleft()
                self.mouseHistory.append(MouseHistoryEntry(self.kineticPanSpeed, currentTime))

            self.panFloatWrapper(delta)

        event.accept()

    def kineticTimerEvent(self):

        currentTime = QTime.currentTime()

        deltaTime = self.lastMoveTime.msecsTo(currentTime)
        self.lastMoveTime = currentTime

        if self.panDecellerate:
            self.kineticPanSpeed *= pow(0.5, float(deltaTime / KINETIC_PANNING_HALFLIFE))

        scaledSpeed = self.kineticPanSpeed * deltaTime

        if self.kineticPanSpeed.manhattanLength() < KINETIC_PAN_SPEED_THRESHOLD:
            self.kineticTimer.stop()
            return
        self.panFloatWrapper(scaledSpeed)

    def panFloatWrapper(self, delta):

        self.remainingPan += delta
        move = self.remainingPan.toPoint()
        self.pan(move.x(), move.y())
        self.remainingPan -= move

    def mouseDoubleClickEvent(self, event):

        self.setFocus()

        self.pan(event.lastPos().x() - self.size().width() / 2.0, event.lastPos().y() - self.size().height() / 2.0)

        if self.zoomLevel() < self.maximumZoomLevel():
            self.setZoomLevel(self.zoomLevel() + 1)

        event.accept()

    def keyPressEvent(self, event):

        if event.key() == Qt.Key_Minus:
            if self.zoomLevel() > self.minimumZoomLevel():
                self.setZoomLevel(self.zoomLevel() - 1)
        elif event.key() == Qt.Key_Plus:
            if self.zoomLevel() < self.maximumZoomLevel():
                self.setZoomLevel(self.zoomLevel() + 1)
        elif event.key() == Qt.Key_T:
            if self.mapType() == QGraphicsGeoMap.StreetMap:
                self.setMapType(QGraphicsGeoMap.SatelliteMapDay)
            elif self.mapType() == QGraphicsGeoMap.SatelliteMapDay:
                self.setMapType(QGraphicsGeoMap.StreetMap)

        event.accept()

    def wheelEvent(self, event):

        if event.delta() > 0:
            if self.zoomLevel() < self.maximumZoomLevel():
                self.setZoomLevel(self.zoomLevel() + 1)
        else:
            if self.zoomLevel() > self.minimumZoomLevel():
                self.setZoomLevel(self.zoomLevel() - 1)

        event.accept()
Beispiel #57
0
class TextEditor(RWWidget, Ui_Form):
    """ Contains many features of popular text editors adapted for use with
    Ontologies such as syntax highlighting, and autocompletion. One column on
    the left of the text editor contains line numbers and another contains
    other contextual information such as whether a block of code has been
    hidden/collapsed and can be displayed/expanded later.  It also contains an
    incremental search and an interface to pySUMO's settings so font size and
    family can be changed at will.

    Variables:

    - syntax_highlighter: The syntax highlighter object for the text editor.

    Methods:
    
    - __init__: Initalizes the Object and the QPlainTextEdit
    - commit: Notifies other Widgets of changes.
    - show_autocomplete: Returns autocompletion choices.
    - getWidget: returns the QPlainTextEdit
    - numberbarPaint: Paints the numberbar
    - searchCompletion: Asks QCompleter if a whole word exists starting with user input
    - hideFrom: Starts hides all lines from the ()-block started by line
    - insertCompletion: Puts the selected Completion into the TextEditor
    """
    def __init__(self, mainwindow, settings=None):
        """ Initializes the text editor widget. """
        super(TextEditor, self).__init__(mainwindow)
        self.setupUi(self.mw)
        self.plainTextEdit.clear()
        self.plainTextEdit.setEnabled(False)
        self.plainTextEdit.show()
        self.highlighter = SyntaxHighlighter(self.plainTextEdit.document(),
                                             settings)
        self.initAutocomplete()

        self._initNumberBar()
        self.hidden = {}
        self.printer = QPrinter(QPrinterInfo.defaultPrinter())
        self.plainTextEdit.setTextCursor(
            self.plainTextEdit.cursorForPosition(QPoint(0, 0)))

        self.canUndo = False
        self.canRedo = False

        self.ontologySelector.setCurrentIndex(-1)

        self.timer = QTimer(self)
        self.timer.setSingleShot(True)
        self.timer.timeout.connect(self.commit)

        #Connects
        self.getWidget().textChanged.connect(self.searchCompletion)
        self.plainTextEdit.undoAvailable.connect(self.setCanUndo)
        self.plainTextEdit.redoAvailable.connect(self.setCanRedo)

        self.ontologySelector.currentIndexChanged[int].connect(
            self.showOtherOntology)
        self.plainTextEdit.textChanged.connect(self.expandIfBracketRemoved)

        self.plainTextEdit.textChanged.connect(self.setTextChanged)

        self._updateOntologySelector()  #must be after connects

    @Slot()
    def setTextChanged(self):
        """Is called if the text changed signal is thrown and 
        sets a timer of 3 seconds to reparse the ontology. """
        self.timer.stop()
        self.timer.start(3000)

    def setCanUndo(self, b):
        self.canUndo = b

    def setCanRedo(self, b):
        self.canRedo = b

    def _print_(self):
        """ Creates a print dialog with the latest text"""
        dialog = QPrintDialog()
        if dialog.exec_() == QDialog.Accepted:
            doc = self.plainTextEdit.document()
            doc.print_(dialog.printer())

    def _quickPrint_(self):
        """ No dialog, just print"""
        if self.printer is None:
            return
        doc = self.plainTextEdit.document()
        doc.print_(self.printer)

    def _printPreview_(self):
        """ Create a print preview"""
        dialog = QPrintPreviewDialog()
        dialog.paintRequested.connect(self.plainTextEdit.print_)
        dialog.exec_()

    def saveOntology(self):
        """ Save the ontology to disk"""
        idx = self.ontologySelector.currentIndex()
        ontology = self.ontologySelector.itemData(idx)
        if ontology is None:
            return
        if type(ontology) is Ontology:
            ontology.save()

    def getActiveOntology(self):
        idx = self.ontologySelector.currentIndex()
        return self.ontologySelector.itemData(idx)

    def undo(self):
        if self.canUndo:
            self.plainTextEdit.undo()
            try:
                self.SyntaxController.add_ontology(
                    self.getActiveOntology(), self.plainTextEdit.toPlainText())
            except ParseError:
                return
            self.commit()
        else:
            super(TextEditor, self).undo()

    def redo(self):
        if self.canRedo:
            self.plainTextEdit.redo()
            try:
                self.SyntaxController.add_ontology(
                    self.getActiveOntology(), self.plainTextEdit.toPlainText())
            except ParseError:
                return
            self.commit()
        else:
            super(TextEditor, self).redo()

    def _initNumberBar(self):
        """ Init the number bar"""
        self.number_bar = NumberBar(self)
        self.number_bar.setMinimumSize(QSize(30, 0))
        self.number_bar.setObjectName("number_bar")
        self.gridLayout.addWidget(self.number_bar, 1, 0, 1, 1)
        self.plainTextEdit.blockCountChanged.connect(
            self.number_bar.adjustWidth)
        self.plainTextEdit.updateRequest.connect(
            self.number_bar.updateContents)

    @Slot(int)
    def jumpToLocation(self, location, ontology):
        if ontology == str(self.getActiveOntology()):
            textBlock = self.plainTextEdit.document().findBlockByNumber(
                location)
            pos = textBlock.position()
            textCursor = self.plainTextEdit.textCursor()
            textCursor.setPosition(pos)
            self.plainTextEdit.setTextCursor(textCursor)
            self.plainTextEdit.centerCursor()

    def _updateOntologySelector(self):
        """ Update the ontology selector where you can select which Ontology to show in the editor"""
        current = self.ontologySelector.currentText()
        self.ontologySelector.currentIndexChanged[int].disconnect(
            self.showOtherOntology)
        self.ontologySelector.clear()
        index = -1
        count = 0
        for i in self.getIndexAbstractor().ontologies:
            if current == i.name:
                index = count
            self.ontologySelector.addItem(i.name, i)
            count = count + 1
        self.ontologySelector.setCurrentIndex(index)
        # if index == -1 :
        # the ontology was removed.
        #    self.showOtherOntology(index)
        if index == -1:
            self.plainTextEdit.setEnabled(False)
            self.plainTextEdit.clear()
        self.ontologySelector.currentIndexChanged[int].connect(
            self.showOtherOntology)

    def setActiveOntology(self, ontology):
        index = -1
        count = 0
        for i in self.getIndexAbstractor().ontologies:
            if ontology.name == i.name:
                index = count
                break
            count = count + 1
        self.ontologySelector.setCurrentIndex(index)

    @Slot(int)
    def showOtherOntology(self, idx):
        """ Show other ontology in the plaintextedit
            
            Arguments:
            
            - idx: The id of the current Ontologyselector
        """
        dced = False
        try:
            self.plainTextEdit.textChanged.disconnect(self.setTextChanged)
        except RuntimeError:
            dced = True

        idx = self.ontologySelector.currentIndex()

        if idx == -1:
            self.plainTextEdit.setEnabled(False)
            self.plainTextEdit.clear()
            return
        ontologyname = self.ontologySelector.currentText()
        for i in self.getIndexAbstractor().ontologies:
            if i.name == ontologyname:
                self.plainTextEdit.setEnabled(True)
                self.getWidget().setPlainText(
                    self.getIndexAbstractor().get_ontology_file(i).getvalue())

                if not dced:
                    self.plainTextEdit.textChanged.connect(self.setTextChanged)

                return
        self.plainTextEdit.textChanged.connect(self.commit)
        assert False

    @Slot()
    def expandIfBracketRemoved(self):
        """ Check if a line with ( or ) was changed and expand the possible hidden lines   
        """
        current_line = self.getWidget().document().findBlock(
            self.getWidget().textCursor().position()).blockNumber() + 1
        if current_line in self.hidden:
            self.toggleVisibility(current_line)

    @Slot()
    def zoomIn(self):
        """ Increase the size of the font in the TextEditor
        
        """
        doc = self.getWidget().document()
        font = doc.defaultFont()
        font.setPointSize(font.pointSize() + 1)
        font = QFont(font)
        doc.setDefaultFont(font)

    @Slot()
    def zoomOut(self):
        """ Decrease the size of the font in the TextEditor"""
        doc = self.getWidget().document()
        font = doc.defaultFont()
        font.setPointSize(font.pointSize() - 1)
        font = QFont(font)
        doc.setDefaultFont(font)

    @Slot()
    def expandAll(self):
        """ Expands all hidden code blocks"""
        for see in list(self.hidden.keys()):
            self.toggleVisibility(see)

    @Slot()
    def collapseAll(self):
        """ Collapse all code blocks (where possible)"""
        block = self.getWidget().document().firstBlock()
        while block.isValid():
            if block.isVisible():
                if block.text().count("(") > block.text().count(")"):
                    self.toggleVisibility(block.blockNumber() + 1)
            block = block.next()

    def _hideLines(self, lines):
        for line in lines:
            if line == 0:
                continue
            block = self.getWidget().document().findBlockByNumber(line - 1)
            assert block.isVisible()
            block.setVisible(False)
            assert not block.isVisible(), "Problem with line %r" % (line)

    def _showLines(self, lines):
        """ Show the lines not visible starting by lines
        
        Arguments:
        
        - lines: The first line followed by an unvisible block
        
        """
        for line in lines:
            block = self.getWidget().document().findBlockByNumber(line - 1)
            block.setVisible(True)

    def getLayoutWidget(self):
        """ Returns the layout widget"""
        return self.widget

    def numberbarPaint(self, number_bar, event):
        """Paints the line numbers of the code file"""
        self.number_bar.link = []
        font_metrics = self.getWidget().fontMetrics()
        current_line = self.getWidget().document().findBlock(
            self.getWidget().textCursor().position()).blockNumber() + 1

        block = self.getWidget().firstVisibleBlock()
        line_count = block.blockNumber()
        painter = QPainter(self.number_bar)
        # TODO: second argument is color -> to settings
        painter.fillRect(self.number_bar.rect(),
                         self.getWidget().palette().base())

        # Iterate over all visible text blocks in the document.
        while block.isValid():
            line_count += 1
            text = str(line_count)
            block_top = self.getWidget().blockBoundingGeometry(
                block).translated(self.getWidget().contentOffset()).top()
            if not block.isVisible():
                block = block.next()
                while not block.isVisible():
                    line_count += 1
                    block = block.next()
                continue
            self.number_bar.link.append((block_top, line_count))
            # Check if the position of the block is out side of the visible
            # area.
            if block_top >= event.rect().bottom():
                break

            # We want the line number for the selected line to be bold.
            if line_count == current_line:
                font = painter.font()
                font.setBold(True)

            else:
                font = painter.font()
                font.setBold(False)
            # line opens a block
            if line_count in self.hidden:
                text += "+"
                font.setUnderline(True)
            elif block.text().count("(") > block.text().count(")"):
                text += "-"
                font.setUnderline(True)
            else:
                font.setUnderline(False)
            painter.setFont(font)
            # Draw the line number right justified at the position of the
            # line.
            paint_rect = QRect(0, block_top, self.number_bar.width(),
                               font_metrics.height())
            painter.drawText(paint_rect, Qt.AlignLeft, text)
            block = block.next()

        painter.end()

    def initAutocomplete(self):
        """Inits the QCompleter and gives him a list of words"""
        self.completer = QCompleter(
            list(
                OrderedDict.fromkeys(
                    re.split("\\W", self.plainTextEdit.toPlainText()))))
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)
        self.completer.setWidget(self.getWidget())
        self.completer.activated.connect(self.insertCompletion)

    def searchCompletion(self):
        """Searches for possible completion from QCompleter to the current text position"""
        tc = self.getWidget().textCursor()
        tc.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor)
        if tc.selectedText() in string.whitespace:
            self.completer.popup().hide()
            return
        tc.movePosition(QTextCursor.StartOfWord, QTextCursor.KeepAnchor)

        beginning = tc.selectedText()
        if len(beginning) >= 3:
            self.completer.setCompletionPrefix(beginning)
            self.completer.complete()
        shortcut = QShortcut(QKeySequence("Ctrl+Enter"), self.getWidget(),
                             self.insertCompletion)

    def toggleVisibility(self, line):
        """ Shows or hides a line """
        if line in self.hidden:
            self._showLines(self.hidden[line])
            del self.hidden[line]
        else:
            self.hideFrom(line)

        # update views
        self.getWidget().hide()
        self.getWidget().show()
        self.number_bar.update()

    def hideFrom(self, line):
        """ Hides a block starting by line. Do nothing if not hidable"""
        block = self.getWidget().document().findBlockByNumber(line - 1)

        openB = block.text().count("(")
        closeB = block.text().count(")")
        startline = line
        # go to line >= line: block starts counting by 0
        block = self.getWidget().document().findBlockByNumber(line - 1)
        hidden = []
        assert block.isValid()
        while openB > closeB and block.isValid():
            assert block.isValid()
            block = block.next()
            line = block.blockNumber() + 1
            if block.isVisible():
                hidden.append(line)
            openB += block.text().count("(")
            closeB += block.text().count(")")

        if hidden == []:
            return
        self._hideLines(hidden)
        self.hidden[startline] = hidden

        # set current line in viewable area
        current_line = self.getWidget().document().findBlock(
            self.getWidget().textCursor().position()).blockNumber() + 1
        if (startline < current_line and current_line <= line):
            block = block.next()
            cursor = QTextCursor(block)
            self.getWidget().setTextCursor(cursor)

    @Slot(str)
    def insertCompletion(self, completion):
        """ Adds the completion to current text"""
        tc = self.getWidget().textCursor()
        tc.movePosition(QTextCursor.StartOfWord, QTextCursor.KeepAnchor)
        tc.removeSelectedText()
        tc.insertText(completion)

    def getWidget(self):
        """ Return the QPlainTextEdit Widget"""
        return self.plainTextEdit

    @Slot()
    def commit(self):
        """ Overrides commit from RWWidget. """

        idx = self.ontologySelector.currentIndex()
        if idx == -1:
            return
        ontology = self.ontologySelector.itemData(idx)
        if ontology is None:
            return
        try:
            QApplication.setOverrideCursor(Qt.BusyCursor)
            self.SyntaxController.add_ontology(
                ontology, self.plainTextEdit.toPlainText())
            QApplication.setOverrideCursor(Qt.ArrowCursor)
        except ParseError:
            return
        super(TextEditor, self).commit()

    @Slot()
    def refresh(self):
        """ Refreshes the content of the TextEditor (syncing with other widgets)"""
        textCursorPos = self.plainTextEdit.textCursor().position()
        super(TextEditor, self).refresh()
        dced = False
        try:
            self.plainTextEdit.textChanged.disconnect(self.setTextChanged)
        except RuntimeError:
            dced = True
        idx = self.ontologySelector.currentIndex()
        ontology = self.ontologySelector.itemData(idx)
        if ontology in self.IA.ontologies:
            f = self.IA.get_ontology_file(ontology)
            self.plainTextEdit.setPlainText(f.getvalue())
        if not dced:
            self.plainTextEdit.textChanged.connect(self.setTextChanged)
        cursor = self.plainTextEdit.textCursor()
        cursor.setPosition(textCursorPos)
        self.plainTextEdit.setTextCursor(cursor)
        self.plainTextEdit.centerCursor()