Example #1
0
class TarayiciTaramaBaslat(QtGui.QWidget):
    def __init__(self, parent=None):
        super(TarayiciTaramaBaslat, self).__init__(parent)
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.time)
        self.timer.setInterval(1000)
        self.timer.start()
        print("timer başladı")

        google = GoogleTaramayaBasla(parent=self)
        mozilla = MozillaTaramayaBasla(parent=self)

        self.mozillaMaxHistoryId = int(
            MozillaDatabaseController.mozillaMaxIdGetir())
        self.googleMaxHistoryId = GoogleDatabaseController.googleMaxIdGetir()

    def time(self):
        if (User.uid == ""):
            print("timer durdu")
            self.timer.deleteLater()
            self.timer.destroyed()
        mozilla = MozillaTaramayaBasla(parent=self)
        self.mozillaMaxHistoryId = mozilla.tara(
            mozillaMaxHistoryId=self.mozillaMaxHistoryId)

        google = GoogleTaramayaBasla(parent=self)
        self.googleMaxHistoryId = google.tara(
            googleMaxHistoryId=self.googleMaxHistoryId)
Example #2
0
class AsyncCaller(object):
    """Call a Python function after a delay."""
    def __init__(self, delay=10):
        self._delay = delay
        self._timer = None

    def _create_timer(self, f):
        self._timer = QTimer()
        self._timer.timeout.connect(f)
        self._timer.setSingleShot(True)

    def set(self, f):
        """Call a function after a delay, unless another function is set
        in the meantime."""
        self.stop()
        self._create_timer(f)
        self.start()

    def start(self):
        """Start the timer and call the function after a delay."""
        if self._timer:
            self._timer.start(self._delay)

    def stop(self):
        """Stop the current timer if there is one and cancel the async call."""
        if self._timer:
            self._timer.stop()
            self._timer.deleteLater()
Example #3
0
File: qt.py Project: kwikteam/phy
class AsyncCaller(object):
    """Call a Python function after a delay."""
    def __init__(self, delay=10):
        self._delay = delay
        self._timer = None

    def _create_timer(self, f):
        self._timer = QTimer()
        self._timer.timeout.connect(f)
        self._timer.setSingleShot(True)

    def set(self, f):
        """Call a function after a delay, unless another function is set
        in the meantime."""
        self.stop()
        self._create_timer(f)
        self.start()

    def start(self):
        """Start the timer and call the function after a delay."""
        if self._timer:
            self._timer.start(self._delay)

    def stop(self):
        """Stop the current timer if there is one and cancel the async call."""
        if self._timer:
            self._timer.stop()
            self._timer.deleteLater()
class TarayiciTaramaBaslat(QtGui.QWidget):
	def __init__(self,parent=None):
		super(TarayiciTaramaBaslat, self).__init__(parent)
		self.timer = QTimer(self)
		self.timer.timeout.connect(self.time)
		self.timer.setInterval(1000)
		self.timer.start()
		print("timer başladı")

		google=GoogleTaramayaBasla(parent=self)
		mozilla=MozillaTaramayaBasla(parent=self)

		self.mozillaMaxHistoryId=int(MozillaDatabaseController.mozillaMaxIdGetir())
		self.googleMaxHistoryId=GoogleDatabaseController.googleMaxIdGetir()

	def time(self):
		if(User.uid == ""):
			print("timer durdu")
			self.timer.deleteLater()
			self.timer.destroyed()
		mozilla=MozillaTaramayaBasla(parent=self)
		self.mozillaMaxHistoryId=mozilla.tara(mozillaMaxHistoryId=self.mozillaMaxHistoryId)

		google=GoogleTaramayaBasla(parent=self)
		self.googleMaxHistoryId=google.tara(googleMaxHistoryId=self.googleMaxHistoryId)
Example #5
0
class ChatWidget(QWidget):
    PREFERRED_WIDTH = 400
    _URI_REGEX="""
    (
      (
        [A-Za-z][A-Za-z0-9\+\.\-]*:\/\/
        [A-Za-z0-9\.\-]+
        |
        www\.
        [A-Za-z0-9\.\-]+
        \.[A-Za-z]+
      )
      (?::\d+)?
      (
        (?:\/[\+~%\/\.\w\-=]*)
        ?\??(?:[\-\+=&;%@\.\w]*)
        #?(?:[\.\!\/\\w]*)
      )?
    )
    """.replace("\n", "").replace(" ", "")
    
    _MAIL_REGEX="""
    (
      (
        [\-;:&=\+\$,\w\.]+@
        [A-Za-z0-9\.\-]+
        \.[A-Za-z]+
      )
    )
    """.replace("\n", "").replace(" ", "")
    
    _MD_LINK_REGEX="\[[^]]+\]\([^)]+\)"
    
    _URI_MATCHER = QRegExp(_URI_REGEX)
    _MAIL_MATCHER = QRegExp(_MAIL_REGEX)
    _MD_LINK_MATCHER = QRegExp(_MD_LINK_REGEX)
    _TIME_ROW_INTERVAL = 10*60 # once every 10 minutes

    sendMessage = pyqtSignal(object, object) # peer ID, message HTML
    typing = pyqtSignal()
    cleared = pyqtSignal()
        
    def __init__(self, parent, logger, ownName, otherName, ownPicFile, otherPicFile, otherID, sendAction):
        super(ChatWidget, self).__init__(parent)
        
        self.logger = logger
        self._firstShowEvent = True
        
        self._offline = False
        if sendAction is not None:
            self._blocked = sendAction.getPeerState(otherID) == PrivacySettings.STATE_BLOCKED
        else:
            self._blocked = False
        self._delivering = False
        self._lastTimeRow = 0
        self._textChanged = False
        self._keepEntryText = False
        self._markdownEnabled = False
        self._md = None
        
        self._typingTimer = QTimer(self)
        self._typingTimer.timeout.connect(self._checkTyping)
        self._typingTimer.start(1000)
        self._entryWasEmpty = True
        self._selfWasTyping = False
        self._otherWasTyping = False
        self._lastTimeSelfTyped = None
        self._lastTimePartnerTyped = None
        
        self._otherID = otherID
        
        self._otherName = otherName
        self._ownName = ownName
        
        self._sendAction = sendAction
        
        self._errIcon = None
        self._warnIcon = None
        try:
            from PyQt4.QtGui import QCommonStyle, QStyle
            style = QCommonStyle()
            self._errIcon = style.standardIcon(QStyle.SP_MessageBoxCritical)
            if self._errIcon.isNull():
                self._errIcon = None
            self._warnIcon = style.standardIcon(QStyle.SP_MessageBoxWarning)
            if self._warnIcon.isNull():
                self._warnIcon = None
        except:
            pass
        
        if self._errIcon is None:
            self._errIcon = QIcon(get_settings().get_resource("images", "error.png"))
        if self._warnIcon is None:
            self._warnIcon = QIcon(get_settings().get_resource("images", "warning.png"))
        
        self._initMessageModel()
        self._initMessageTable()
        self._initTextEntry()
        
        mainLayout = QVBoxLayout(self)
        mainLayout.setSpacing(0)
        
        self._addTopLayout(mainLayout)
        mainLayout.addWidget(self.table)
        mainLayout.addWidget(self.entry)
        
        # initialize GUI
        self._updateOtherName()
        self._updateOwnName()
        self.setOwnIconPath(ownPicFile)
        self.setOtherIconPath(otherPicFile)
                
        # TODO option to change behavior
        self.entry.returnPressed.connect(self.eventTriggered)
        
        self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.MinimumExpanding)
        
        get_notification_center().connectPeerAppended(self._peerUpdated)
        get_notification_center().connectPeerUpdated(self._peerUpdated)
        get_notification_center().connectPeerRemoved(self._peerRemoved)
        get_notification_center().connectAvatarChanged(self._avatarChanged)
        get_notification_center().connectDisplayedPeerNameChanged(self._displayedPeerNameChanged)
        get_notification_center().connectPrivacySettingsChanged(self._privacySettingsChanged)
        
        if get_peers() != None:
            self._setOffline(not get_peers().isPeerID(pID=self._otherID))
        else:
            self._setOffline(True)
            
        self.setAcceptDrops(True)
        
    def setMarkdownEnabled(self, en):
        self._markdownEnabled = en
        
    def _canSendFilesToOther(self):
        peerInfo = get_peers().getPeerInfo(pID=self._otherID)
        if peerInfo is None:
            return False
        action = PeerActions.get().getPeerActionByName(u"hannesrauhe.lunchinator.file_transfer", u"Send File")
        if action is None or not action.appliesToPeer(self._otherID, peerInfo):
            return False
        return True    
    
    def dragEnterEvent(self, event):
        if not self._canSendFilesToOther():
            event.ignore()
            return
        
        if event.mimeData().hasUrls() and int(event.possibleActions()) & Qt.CopyAction:
            for url in event.mimeData().urls():
                if url.scheme() == u"file":
                    event.setDropAction(Qt.CopyAction)
                    event.accept()
                    return
        event.ignore()
        
    def dropEvent(self, event):
        if not self._canSendFilesToOther():
            event.ignore()
            return
        
        if event.mimeData().hasUrls() and int(event.possibleActions()) & Qt.CopyAction:
            files = []
            for url in event.mimeData().urls():
                if url.scheme() == u"file":
                    files.append(convert_string(url.toLocalFile()))
            
            if len(files) > 0:        
                action = PeerActions.get().getPeerActionByName(u"hannesrauhe.lunchinator.file_transfer", u"Send File")
                action.sendFilesToPeer(files, self._otherID)
                event.accept()
                return
        event.ignore()
        
    def _setOffline(self, offline):
        if self._offline == offline:
            return
        
        self._offline = offline
        self._updateOtherName()
        
    def _updateOtherName(self):
        if self._offline:
            title = self._otherName + " (Offline)"
        elif self._blocked:
            title = self._otherName + " (Blocked)"
        else:
            title = self._otherName
            
        self._otherPicLabel.setEnabled(not self._offline and not self._blocked)
        self._otherNameLabel.setText(title)
        self.parent().setWindowTitle(title)
        self._checkEntryState()
        
    def _updateOwnName(self):
        self._ownNameLabel.setText(self._ownName)
        
    @loggingSlot(object, object)
    def _peerUpdated(self, peerID, peerInfo):
        peerID = convert_string(peerID)
        if peerID == self._otherID:
            self._setOffline(u"PM_v" not in peerInfo)

    @loggingSlot(object)    
    def _peerRemoved(self, peerID):
        peerID = convert_string(peerID)
        if peerID == self._otherID:
            self._setOffline(True)
            
    @loggingSlot(object, object)
    def _avatarChanged(self, peerID, newFile):
        peerID = convert_string(peerID)
        newFile = convert_string(newFile)
        
        if peerID == self._otherID:
            self.setOtherIconPath(get_peers().getPeerAvatarFile(pID=peerID))
        if peerID == get_settings().get_ID():
            self.setOwnIconPath(get_peers().getPeerAvatarFile(pID=peerID))
            
    @loggingSlot(object, object, object)
    def _displayedPeerNameChanged(self, pID, newName, _infoDict):
        pID = convert_string(pID)
        newName = convert_string(newName)
        
        if pID == self._otherID:
            self._otherName = newName
            self._updateOtherName()
        if pID == get_settings().get_ID():
            self._ownName = newName
            self._updateOwnName()
        
    @loggingSlot(object, object)
    def _privacySettingsChanged(self, pluginName, actionName):
        if self._sendAction is None:
            return
        if pluginName == self._sendAction.getPluginName() and \
           actionName == self._sendAction.getName():
            blocked = self._sendAction.getPeerState(self._otherID) == PrivacySettings.STATE_BLOCKED
            if blocked != self._blocked:
                self._blocked = blocked
                self._updateOtherName()
        
    def _clearEntry(self):
        self.entry.clear()
        self.entry.setCurrentCharFormat(QTextCharFormat())
        
    def _checkEntryState(self):
        self.entry.setEnabled(not self._offline and not self._delivering and not self._blocked)
        if self._offline or self._blocked:
            if self.entry.document().isEmpty():
                self._clearEntry()
                if self._offline:
                    self.entry.setText(u"Partner is offline")
                else:
                    self.entry.setText(u"Partner is blocked")
            else:
                self._keepEntryText = True
        elif self._delivering:
            self._clearEntry()
            self.entry.setText(u"Delivering...")
        elif not self._keepEntryText:
            self._clearEntry()
            
        if self._keepEntryText and (not self._offline and not self._blocked):
            # reset if not offline any more
            self._keepEntryText = False
        
    def nextInFocusChain(self):
        return self.entry
    
    def previousInFocusChain(self):
        return self.entry
    
    def focusInEvent(self, _event):
        self.entry.setFocus(Qt.OtherFocusReason)
        
    def _filterPeerAction(self, pluginName, action):
        return pluginName != u"hannesrauhe.lunchinator.private_messages" or action.getName() != "Open Chat"
        
    def _addTopLayout(self, mainLayout):
        topWidget = QWidget(self)
        topLayout = QHBoxLayout(topWidget)
        topLayout.setSpacing(0)
        topLayout.setContentsMargins(0, 0, 0, 0)
        
        self._otherNameLabel = QToolButton(topWidget)
        self._otherNameLabel.setToolButtonStyle(Qt.ToolButtonTextOnly)
        self._otherNameLabel.setStyleSheet("QToolButton { text-align: left; font-size: 13pt; border: none; margin-left: -5px; padding-right:5px; padding-bottom: -2px;}")
        self._otherNameLabel.setContextMenuPolicy(Qt.CustomContextMenu)
        self._otherNameLabel.customContextMenuRequested.connect(partial(showPeerActionsPopup, self._otherID, self._filterPeerAction, self._otherNameLabel))
        self._otherNameLabel.setPopupMode(QToolButton.InstantPopup)
        menu = QMenu(self._otherNameLabel)
        menu.aboutToShow.connect(partial(initializePeerActionsMenu, menu, self._otherID, self._filterPeerAction, self))
        self._otherNameLabel.setMenu(menu)
        
        self._otherPicLabel = QLabel(topWidget)
        topLayout.addWidget(self._otherPicLabel, 0, Qt.AlignLeft)
        topLayout.addSpacing(5)
        topLayout.addWidget(self._otherNameLabel, 0, Qt.AlignLeft)
        
        self._otherStatusLabel = QLabel(topWidget)
        topLayout.addSpacing(2)
        topLayout.addWidget(self._otherStatusLabel, 1, Qt.AlignLeft)
        
        self._ownNameLabel = QToolButton(topWidget)
        self._ownNameLabel.setStyleSheet("QToolButton { text-align: left; font-size: 13pt; border: none; margin-right: -5px;}")
        self._ownNameLabel.setToolButtonStyle(Qt.ToolButtonTextOnly)
        self._ownPicLabel = QLabel(topWidget)
        topLayout.addWidget(self._ownNameLabel, 1, Qt.AlignRight)
        topLayout.addSpacing(5)
        topLayout.addWidget(self._ownPicLabel, 0, Qt.AlignRight)
        
        mainLayout.addWidget(topWidget)
        separator = QFrame(self)
        separator.setFrameShape(QFrame.HLine)
        separator.setFrameShadow(QFrame.Sunken)
        mainLayout.addSpacing(5)
        mainLayout.addWidget(separator)
        mainLayout.addSpacing(5)
        
    def _initTextEntry(self):
        self.entry = HistoryTextEdit(self, True)
        self.entry.textChanged.connect(self._textChangedSlot)
        
    def _initMessageModel(self):
        self._model = ChatMessagesModel(self, self)
        
    def _initMessageTable(self):
        self.table = ChatMessagesView(self._model, self, self.logger)
        self.table.setContextMenuPolicy(Qt.CustomContextMenu)
        self.table.customContextMenuRequested.connect(self._showContextMenu)
        
    @loggingSlot(QPoint)
    def _showContextMenu(self, point):
        index = self.table.indexAt(point)
        if index != None:
            if index.column() != ChatMessagesModel.MESSAGE_COLUMN:
                return
            isOwn = index.data(ChatMessagesModel.OWN_MESSAGE_ROLE).toBool()
            menu = QMenu(self)
            if isOwn:
                menu.addAction(u"Send again", partial(self._sendAgain, index))
            menu.addAction(u"Copy", partial(self._copy, index))
            menu.exec_(QCursor.pos())
            menu.deleteLater()
        
    @loggingSlot()
    def _sendAgain(self, index):
        self.sendMessage.emit(self._otherID, convert_string(index.data(Qt.DisplayRole).toString()))
        
    @loggingSlot()
    def _copy(self, index):
        doc = QTextDocument()
        doc.setHtml(index.data(Qt.DisplayRole).toString())
        clipboard = QApplication.clipboard()
        richTextData = QMimeData()
        richTextData.setHtml(index.data(Qt.DisplayRole).toString())
        richTextData.setText(doc.toPlainText())
        clipboard.setMimeData(richTextData)
        
    @loggingSlot()
    def _textChangedSlot(self):
        if not self.entry.isEnabled() or self.entry.document().isEmpty():
            self._textChanged = False
            if not self._entryWasEmpty:
                self._entryWasEmpty = True
                self._selfWasTyping = False
                self._informCleared()
        elif not self._selfWasTyping:
            self._entryWasEmpty = False
            self._informTyping()
            self._selfWasTyping = True
            self._lastTimeSelfTyped = time()
        else:
            self._textChanged = True
            
    @loggingSlot()
    def _checkTyping(self):
        curTime = time()
        if self._textChanged:
            self._informTyping()
            # TODO do we really need thread safety here?
            self._textChanged = False
            self._lastTimeSelfTyped = curTime
        elif self._selfWasTyping and curTime - self._lastTimeSelfTyped > 3:
            self._selfWasTyping = False    
            
        if self._otherWasTyping and curTime - self._lastTimePartnerTyped > 3:
            self.setStatus("paused typing")
            self._otherWasTyping = False
            
    def _informTyping(self):
        if not self._offline and not self._blocked:
            self.typing.emit()
        
    def _informCleared(self):
        if not self._offline:
            self.cleared.emit()
            
    @loggingSlot()
    def otherIsTyping(self):
        if not self._otherWasTyping:
            self._otherWasTyping = True
            self.setStatus("typing...")
        self._lastTimePartnerTyped = time()
    
    @loggingSlot()
    def otherCleared(self):
        self._otherWasTyping = False
        self.setStatus(None)
       
    def showEvent(self, event):
        QWidget.showEvent(self, event)
        if self._firstShowEvent:
            self._firstShowEvent = False
            # relayout (i don't know why this is necessary)
            self.table.reset()
       
    def addTimeRow(self, rtime):
        self._model.addTimeRow(rtime)
        
    def _checkTime(self, msgTime):
        if msgTime - self._lastTimeRow > self._TIME_ROW_INTERVAL:
            self.addTimeRow(msgTime)
            self._lastTimeRow = msgTime
        
    @loggingFunc
    def addOwnMessage(self, msgID, recvTime, msg, msgTime, messageState=None, toolTip=None):
        self._checkTime(msgTime)
        self._model.addOwnMessage(msgID, recvTime, msg, msgTime, messageState, toolTip)
        self.entry.clear()
        self._delivering = False
        self._checkEntryState()
        self.entry.setFocus(Qt.OtherFocusReason)
        self.table.setScrollToEnd(True)
        
    def addOtherMessage(self, msg, msgTime, recvTime):
        self._checkTime(msgTime)
        self._model.addOtherMessage(msg, msgTime, recvTime)
        
    def delayedDelivery(self, msgID, recvTime, error, errorMessage):
        return self._model.messageDelivered(msgID, recvTime, error, errorMessage)
        
    def messageIDChanged(self, oldID, newID):
        self._model.messageIDChanged(oldID, newID)
        
    def canClose(self):
        return not self._delivering
    
    def finish(self):
        get_notification_center().disconnectPeerAppended(self._peerUpdated)
        get_notification_center().disconnectPeerUpdated(self._peerUpdated)
        get_notification_center().disconnectPeerRemoved(self._peerRemoved)
        get_notification_center().disconnectAvatarChanged(self._avatarChanged)
        get_notification_center().disconnectDisplayedPeerNameChanged(self._displayedPeerNameChanged)
        
        if self._typingTimer is not None:
            self._typingTimer.stop()
            self._typingTimer.deleteLater()
            self._typingTimer = None
        
    def getOwnIcon(self):
        return self._ownIcon    
    
    def getOwnIconPath(self):
        return self._ownIconPath
    def setOwnIconPath(self, iconPath):
        if not iconPath:
            iconPath = get_settings().get_resource("images", "me.png")
        self._ownIconPath = iconPath
        self._ownIcon = QIcon(iconPath)
        self._ownPicLabel.setPixmap(QPixmap(self._ownIconPath).scaled(24,24, Qt.KeepAspectRatio, Qt.SmoothTransformation))
        self._model.setOwnIcon(self._ownIcon)

    def getOtherIcon(self):
        return self._otherIcon        
    
    def getOtherIconPath(self):
        return self._otherIconPath
    def setOtherIconPath(self, iconPath):
        if not iconPath:
            iconPath = get_settings().get_resource("images", "lunchinator.png")
        self._otherIconPath = iconPath
        self._otherIcon = QIcon(iconPath)
        self._otherPicLabel.setPixmap(QPixmap(self._otherIconPath).scaled(24,24, Qt.KeepAspectRatio, Qt.SmoothTransformation))
        self._model.setOtherIcon(self._otherIcon)
        
    def getOtherName(self):
        return self._otherName
        
    def getWarnIcon(self):
        return self._warnIcon
    
    def getErrorIcon(self):
        return self._errIcon
    
    def setStatus(self, statusText):
        if statusText:
            title = u"(%s)" % (statusText)
        else:
            title = u""
        self._otherStatusLabel.setText(title)
        
    def _getMD(self):
        if self._md is None:
            try:
                from markdown import Markdown
                if getPlatform()==PLATFORM_WINDOWS and isPyinstallerBuild():
                    self._md = Markdown()
                else:
                    self._md = Markdown(extensions=['extra'])
            except ImportError:
                self.logger.error("Cannot enable Markdown (%s)", formatException())
                raise
        return self._md
    
    @loggingSlot()        
    def eventTriggered(self):
        if self.entry.toPlainText().trimmed().length() is 0:
            return
        
        text = None
        if self._markdownEnabled:
            try:
                md = self._getMD()
                text = self._detectHyperlinks(True)
                text = md.convert(text)
            except ImportError:
                pass
        if text is None:
            # fallback to default method
            self._detectHyperlinks()
            text = self._cleanHTML(convert_string(self.entry.toHtml()))
        self.sendMessage.emit(self._otherID, text)
        self._delivering = True
        self._checkEntryState()

    def _cleanHTML(self, html):
        # only body, no paragraph attributes
        html = html.encode("utf-8")
        cleaned = u""
        e = ElementTree.fromstring(html)
        body = e.getiterator("html")[0].getiterator("body")[0]
        for p in body.getiterator("p"):
            p.attrib = {}
            sio = StringIO()
            ElementTree.ElementTree(p).write(sio, "utf-8")
            cleaned += sio.getvalue().replace('<br />', '').decode("utf-8")
            
        return cleaned

    def _iterMatchedRanges(self, matcher, text):
        pos = 0
        while pos != -1:
            pos = matcher.indexIn(text, pos)
            if pos == -1:
                break
            
            yield pos, pos + matcher.matchedLength(), matcher
            
            pos += matcher.matchedLength()

    def _insertAnchors(self, cursor, plainText, matcher, hrefFunc):
        for start, end, matcher in self._iterMatchedRanges(matcher, plainText):
            cursor.setPosition(start);
            cursor.setPosition(end, QTextCursor.KeepAnchor)
    
            fmt = QTextCharFormat()
            fmt.setAnchor(True)
            fmt.setAnchorHref(hrefFunc(matcher.cap()))
            cursor.mergeCharFormat(fmt)
            
    def _findPlainLinks(self, plainText, matcher, hrefFunc, mdLinks):
        newLinks = []
        for start, end, matcher in self._iterMatchedRanges(matcher, plainText):
            if start in mdLinks:
                # is already a markdown link
                continue
            newLinks.append((start, end, hrefFunc(matcher.cap())))
        return newLinks
            
    def _detectHyperlinks(self, toMarkdown=False):
        cursor = QTextCursor(self.entry.document())
        plainText = self.entry.toPlainText()
        
        makeURL = lambda uri : u"http://" + convert_string(uri) if uri.startsWith(u"www.") else uri
        makeMail = lambda mail : u"mailto:" + convert_string(mail)
        if toMarkdown:
            # find markdown links
            mdLinks = IndexSet()
            for start, end, _matcher in self._iterMatchedRanges(self._MD_LINK_MATCHER, plainText):
                mdLinks.addRange(start, end)
            
            # replace plain links with markdown links
            plainLinks = self._findPlainLinks(plainText,
                                              self._URI_MATCHER,
                                              makeURL,
                                              mdLinks)
            plainLinks += self._findPlainLinks(plainText,
                                               self._MAIL_MATCHER,
                                               makeMail,
                                               mdLinks)
            plainLinks = sorted(plainLinks, key=lambda aTup : aTup[0])
            
            plainText = convert_string(plainText)
            for start, end, linkTarget in reversed(plainLinks):
                mdLink = "[%s](%s)" % (plainText[start:end], linkTarget)
                plainText = plainText[:start] + mdLink + plainText[end:]
            return plainText    
        else:
            self._insertAnchors(cursor,
                                plainText,
                                self._URI_MATCHER,
                                makeURL)
            self._insertAnchors(cursor,
                                plainText,
                                self._MAIL_MATCHER,
                                makeMail)
        
    def sizeHint(self):
        sizeHint = QWidget.sizeHint(self)
        return QSize(self.PREFERRED_WIDTH, sizeHint.height())
Example #6
0
class members_table(iface_gui_plugin):
    def __init__(self):
        super(members_table, self).__init__()
        self.membersTable = None
    
    def activate(self):
        iface_gui_plugin.activate(self)
        
    def deactivate(self):
        iface_gui_plugin.deactivate(self)
        
    def get_displayed_name(self):
        return "Peers"
        
    def addHostClicked(self, text):
        if get_server().controller != None:
            get_server().controller.addHostClicked(text)        
    
    def create_widget(self, parent):
        from PyQt4.QtGui import QSortFilterProxyModel
        from PyQt4.QtCore import QTimer, Qt
        from members_table.members_table_model import MembersTableModel
        from lunchinator.table_widget import TableWidget
        
        class NameSortProxyModel(QSortFilterProxyModel):
            def lessThan(self, left, right):
                # compare by lunch time
                ldata = self.sourceModel().data(left, MembersTableModel.SORT_ROLE)
                rdata = self.sourceModel().data(right, MembersTableModel.SORT_ROLE)
                if ldata != rdata:
                    return super(NameSortProxyModel, self).lessThan(left, right)
                
                # compare by name, case sensitive
                lindex = self.sourceModel().index(left.row(), MembersTableModel.NAME_COL_INDEX)
                rindex = self.sourceModel().index(right.row(), MembersTableModel.NAME_COL_INDEX)
                
                res = super(NameSortProxyModel, self).lessThan(lindex, rindex)
                if res or super(NameSortProxyModel, self).lessThan(rindex, lindex):
                    return res
                
                # compare by name, byte order
                ls = convert_string(self.sourceModel().data(lindex, MembersTableModel.SORT_ROLE).toString())
                rs = convert_string(self.sourceModel().data(rindex, MembersTableModel.SORT_ROLE).toString())
                if ls != rs:
                    return ls < rs
                
                # compare by peer ID 
                return self.sourceModel().keys[left.row()] < self.sourceModel().keys[right.row()]
        
        self.membersTable = TableWidget(parent, "Add Host", self.addHostClicked, sortedColumn=MembersTableModel.LUNCH_TIME_COL_INDEX, placeholderText="Enter hostname")
        self.membersTable.getTable().setContextMenuPolicy(Qt.CustomContextMenu)
        self.membersTable.getTable().customContextMenuRequested.connect(self._showContextMenu)
        
        # initialize members table
        self.membersModel = MembersTableModel(get_peers(), self.logger)
        self.membersProxyModel = NameSortProxyModel(self.membersTable)
        self.membersProxyModel.setSortCaseSensitivity(Qt.CaseInsensitive)
        self.membersProxyModel.setSortRole(MembersTableModel.SORT_ROLE)
        self.membersProxyModel.setDynamicSortFilter(True)
        self.membersProxyModel.setSourceModel(self.membersModel)
        self.membersTable.setModel(self.membersProxyModel)
        
        self.membersTable.setColumnWidth(MembersTableModel.NAME_COL_INDEX, 150)
        self.membersTable.setColumnWidth(MembersTableModel.GROUP_COL_INDEX, 150)
        
        get_notification_center().connectPeerAppended(self.membersModel.externalRowAppended)
        get_notification_center().connectPeerUpdated(self.membersModel.externalRowUpdated)
        get_notification_center().connectPeerRemoved(self.membersModel.externalRowRemoved)
        
        get_notification_center().connectMemberAppended(self._updatePeer)
        get_notification_center().connectMemberRemoved(self._updatePeer)
        
        get_notification_center().connectDisplayedPeerNameChanged(self._displayedNameChanged)
        
        self._lunchTimeColumnTimer = QTimer(self.membersModel)
        self._lunchTimeColumnTimer.timeout.connect(self._startSyncedTimer)
        self._lunchTimeColumnTimer.start(msecUntilNextMinute())
        
        return self.membersTable
    
    @loggingFunc
    def _displayedNameChanged(self, peerID, _newName, infoDict):
        self._updatePeer(peerID, infoDict)

    def _updatePeer(self, peerID, infoDict=None):
        peerID = convert_string(peerID)
        if infoDict == None:
            infoDict = get_peers().getPeerInfo(pID=peerID)
        
        if infoDict == None:
            #this happens when a peer that is also a member is removed 
            #-> the peer is not there anymore when the member removed signal arrives
            return
        
        self.membersModel.externalRowUpdated(peerID, infoDict)
    
    @loggingFunc
    def _startSyncedTimer(self):
        self.membersModel.updateLunchTimeColumn()
        self._lunchTimeColumnTimer.timeout.disconnect(self._startSyncedTimer)
        self._lunchTimeColumnTimer.timeout.connect(self.membersModel.updateLunchTimeColumn)
        self._lunchTimeColumnTimer.start(60000)

    @loggingFunc
    def _showContextMenu(self, point):
        from PyQt4.QtGui import QMenu, QCursor
        index = self.membersTable.getTable().indexAt(point)
        index = self.membersProxyModel.mapToSource(index)
        if index != None:
            peerID = self.membersModel.keyAtIndex(index)
            peer_action_utils.showPeerActionsPopup(peerID, lambda _pluginName, _action : True, self.membersTable.getTable())

    def destroy_widget(self):
        iface_gui_plugin.destroy_widget(self)
        
        get_notification_center().disconnectPeerAppended(self.membersModel.externalRowAppended)
        get_notification_center().disconnectPeerUpdated(self.membersModel.externalRowUpdated)
        get_notification_center().disconnectPeerRemoved(self.membersModel.externalRowRemoved)

        get_notification_center().disconnectMemberAppended(self._updatePeer)
        get_notification_center().disconnectMemberRemoved(self._updatePeer)
        
        get_notification_center().disconnectDisplayedPeerNameChanged(self._displayedNameChanged)
        
        self._lunchTimeColumnTimer.stop()
        self._lunchTimeColumnTimer.deleteLater()

        self.membersTable = None
        self.membersModel = None
        self.membersProxyModel = None
        
    def add_menu(self,menu):
        pass
Example #7
0
class online_update(iface_general_plugin):
    CHECK_INTERVAL = 12 * 60 * 60 * 1000 # check twice a day
    
    def __init__(self):
        super(online_update, self).__init__()
        self.hidden_options = {"check_url": "http://update.lunchinator.de"}
        self._scheduleTimer = None
        self.force_activation = True
        self._appUpdateHandler = None
        self._repoUpdateHandler = None
    
    def activate(self):
        iface_general_plugin.activate(self)
        
        try:
            from PyQt4.QtCore import QTimer
            from online_update.appupdate.git_update import GitUpdateHandler
            from online_update.appupdate.mac_update import MacUpdateHandler
            from online_update.appupdate.external_update import ExternalUpdateHandler
            from online_update.appupdate.win_update import WinUpdateHandler
            from online_update.appupdate.app_update_handler import AppUpdateHandler
            from online_update.repoupdate.repo_update_handler import RepoUpdateHandler
            self._activated = True
        except ImportError:
            self._activated = False
            self.logger.warning("ImportError, cannot activate Auto Update")
            return
        
        if GitUpdateHandler.appliesToConfiguration(self.logger):
            self._appUpdateHandler = GitUpdateHandler(self.logger)
        elif MacUpdateHandler.appliesToConfiguration(self.logger):
            self._appUpdateHandler = MacUpdateHandler(self.logger, self.hidden_options["check_url"])
        elif ExternalUpdateHandler.appliesToConfiguration(self.logger):
            self._appUpdateHandler = ExternalUpdateHandler(self.logger)
        elif WinUpdateHandler.appliesToConfiguration(self.logger):
            self._appUpdateHandler = WinUpdateHandler(self.logger, self.hidden_options["check_url"])
        else:
            self._appUpdateHandler = AppUpdateHandler(self.logger)
            
        self._repoUpdateHandler = RepoUpdateHandler(self.logger)
            
        self._appUpdateHandler.activate()
        self._repoUpdateHandler.activate()
        
        get_notification_center().connectInstallUpdates(self.installUpdates)
        get_notification_center().connectRepositoriesChanged(self._repoUpdateHandler.checkForUpdates)
            
        if lunchinator_has_gui():
            self._scheduleTimer = QTimer(getValidQtParent())
            self._scheduleTimer.timeout.connect(self.checkForUpdate)
            self._scheduleTimer.start(online_update.CHECK_INTERVAL)
        
    def deactivate(self):
        if self._activated:
            if self._scheduleTimer is not None:
                self._scheduleTimer.stop()
                self._scheduleTimer.deleteLater()
                
            get_notification_center().emitUpdatesDisabled()
            get_notification_center().disconnectInstallUpdates(self.installUpdates)
            get_notification_center().disconnectRepositoriesChanged(self._repoUpdateHandler.checkForUpdates)
            
            if self._appUpdateHandler is not None:
                self._appUpdateHandler.deactivate()
            if self._repoUpdateHandler is not None:
                self._repoUpdateHandler.deactivate()
        iface_general_plugin.deactivate(self)
    
    def has_options_widget(self):
        return True
    
    def create_options_widget(self, parent):
        from online_update.online_update_gui import OnlineUpdateGUI
        self._ui = OnlineUpdateGUI(self._appUpdateHandler.getInstalledVersion(), parent)
        self._ui.setCanCheckForAppUpdate(self._appUpdateHandler.canCheckForUpdate())
        self._ui.installUpdates.connect(self.installUpdates)
        
        self._appUpdateHandler.setUI(self._ui)
        self._repoUpdateHandler.setUI(self._ui)
        
        return self._ui
    
    def destroy_options_widget(self):
        self._ui.installUpdates.disconnect(self.installUpdates)
        iface_general_plugin.destroy_options_widget(self)
        
    @loggingFunc
    def checkForUpdate(self):
        if self._appUpdateHandler.canCheckForUpdate():
            self._appUpdateHandler.checkForUpdate()
        self._repoUpdateHandler.checkForUpdates()
        
    @loggingFunc
    def installUpdates(self):
        commands = Commands(self.logger)
        
        if self._appUpdateHandler.isInstallReady():
            self._appUpdateHandler.prepareInstallation(commands)
            self._appUpdateHandler.executeInstallation(commands)
        if self._repoUpdateHandler.areUpdatesAvailable():
            self._repoUpdateHandler.prepareInstallation(commands)            
            restartWithCommands(commands, self.logger)
class FileTransferHandler(QObject):
    startOutgoingTransfer = pyqtSignal(int, object, object, object, int, int, bool) # transfer ID, target peer ID, filesOrData, target dir, num files, file size, is retry
    outgoingTransferStarted = pyqtSignal(int, object) # transferID, data thread
    outgoingTransferCanceled = pyqtSignal(int, bool) # transferID, is timeout
    incomingTransferStarted = pyqtSignal(object, int, object, int, int, object, object) # peer ID, transferID, target dir, num files, file size, name, data thread
    
    # private signals
    _processSendRequest = pyqtSignal(object, object, object, object)
    _processCancel = pyqtSignal(object, object)
    _processAck = pyqtSignal(object, object, object)
    _sendFilesToPeer = pyqtSignal(object, object)
    _downloadDirChanged = pyqtSignal(object)
    _overwriteChanged = pyqtSignal(bool)
    _compressionChanged = pyqtSignal(object)
    
    def __init__(self, logger, downloadDir, overwrite, compression):
        super(FileTransferHandler, self).__init__()
        
        self.logger = logger
        self._nextID = 0
        self._downloadDir = downloadDir
        self._overwrite = overwrite
        self._setCompression(compression)

        self._processSendRequest.connect(self._processSendRequestSlot)
        self._processCancel.connect(self._processCancelSlot)
        self._processAck.connect(self._processAckSlot)
        self._sendFilesToPeer.connect(self._sendFilesToPeerSlot)
        self._downloadDirChanged.connect(self._downloadDirChangedSlot)
        self._overwriteChanged.connect(self._overwriteChangedSlot)
        self._compressionChanged.connect(self._compressionChangedSlot)
        
        self._incoming = {} # (peer ID, transfer ID) -> DataReceiverThread
        self._outgoing = {} # transfer ID -> (target ID, file path, start time) or DataSenderThread
        
        self._cleanupTimer = QTimer(self)
        self._cleanupTimer.timeout.connect(self._cleanup)
        self._cleanupTimer.start(10000)
        
    def deactivate(self):
        for aDict in (self._incoming, self._outgoing):
            for data in aDict.values():
                if isinstance(data, DataThreadBase):
                    data.cancelTransfer()
                    data.wait()
            
        if self._cleanupTimer is not None and self._cleanupTimer.isActive():
            self._cleanupTimer.stop()
            self._cleanupTimer.deleteLater()
            self._cleanupTimer = None
    
    def _setCompression(self, comp):
        if type(comp) is unicode:
            comp = comp.encode("utf-8")
        comp = comp.lower()
        if comp == "no":
            self._compression = ""
        elif comp == "gzip":
            self._compression = "gz"
        elif comp == "bip2":
            self._compression = "bzip2"
        
    @loggingSlot()
    def _cleanup(self):
        timedOut = []
        for tID, data in self._outgoing.iteritems():
            if type(data) is tuple:
                _pID, _paths, _dict, startTime = data
                if time() - startTime > 70:
                    timedOut.append(tID)
        for tID in timedOut:
            self._outgoing.pop(tID, None)
            self.outgoingTransferCanceled.emit(tID, True)
    
    def _getNextID(self):
        nextID = self._nextID
        self._nextID += 1
        return nextID
    
    @loggingSlot(QThread, object)
    def _errorDownloading(self, thread, _message):
        self._removeDownload(thread)
            
    @loggingSlot(QThread)
    def _transferCanceled(self, thread):
        peerID, transferID = thread.getUserData()
        sendCancel = False
        isUpload = False
        if type(thread) is DataSenderThread:
            if transferID in self._outgoing:
                del self._outgoing[transferID]
                sendCancel = True
                isUpload = True
        else:
            if (peerID, transferID) in self._incoming:
                del self._incoming[(peerID, transferID)]
                sendCancel = True
        if sendCancel:
            cancelDict = {u"id" : transferID,
                          u"up" : isUpload}
            get_server().call("HELO_FT_CANCEL %s" % json.dumps(cancelDict), peerIDs=[peerID])
    
    @pyqtSlot(QThread)
    @loggingSlot(QThread, object)
    def _removeUpload(self, thread, _msg=None):
        _, transferID = thread.getUserData()
        self._outgoing.pop(transferID, None)
        
    @pyqtSlot(QThread)
    @loggingSlot(QThread, object)
    def _removeDownload(self, thread, _path=None):
        peerID, transferID = thread.getUserData()
        self._incoming.pop((peerID, transferID), None)
        
    ############### PUBLIC INTERFACE ################
    
    # someone wants to send me a file
    def processSendRequest(self, peerID, peerIP, value, preprocessed):
        self._processSendRequest.emit(peerID, peerIP, value, preprocessed)
    @loggingSlot(object, object, object, object)
    def _processSendRequestSlot(self, peerID, peerIP, value, transferDict):
        if transferDict is None:
            try:
                transferDict = json.loads(value)
                
                if not type(transferDict) is dict:
                    self.logger.warning("transferDict is no dict.")
                    return
            except:
                self.logger.exception("Could not parse transfer dict.")
                return
        
        if not u"id" in transferDict:
            self.logger.warning("No transfer ID in transfer dict. Cannot accept request")
            return
        
        if not u"name" in transferDict:
            self.logger.warning("No file name in transfer dict. Cannot accept request")
            return
        
        if not u"size" in transferDict:
            self.logger.warning("No file size in transfer dict. Cannot accept request")
            return
        
        transferID = transferDict[u"id"]
        size = transferDict[u"size"]
        name = transferDict.get(u"name", None)
        numFiles = transferDict.get(u"count", 1)
        
        if ((peerID, transferID) in self._incoming):
            self.logger.debug(u"Ignoring duplicate file transfer from peer %s (%d)", peerID, transferID)
            return
        
        if not os.path.exists(self._downloadDir):
            os.makedirs(self._downloadDir)
        elif not os.path.isdir(self._downloadDir):
            self.logger.error("Download path %s is no directory. Cannot accept file.")
            return
        
        if numFiles > 1:
            peerName = get_peers().getDisplayedPeerName(pID=peerID)
            if peerName:
                dirName = u"%s (%s)" % (sanitizeForFilename(peerName), peerID)
            else:
                dirName = peerID
            targetDir = os.path.join(self._downloadDir, dirName)
            if not os.path.exists(targetDir) or not os.path.isdir(targetDir):
                targetDir = getUniquePath(targetDir)
                os.makedirs(targetDir)
        else:
            targetDir = self._downloadDir
        
        port = DataReceiverThread.getOpenPort(blockPort=True)
        inThread = DataReceiverThread.receive(peerIP, targetDir, port, transferDict, self.logger, overwrite=self._overwrite, parent=self)
        inThread.setUserData((peerID, transferID))
        inThread.errorOnTransfer.connect(self._errorDownloading)
        inThread.successfullyTransferred.connect(self._removeDownload)
        inThread.transferCanceled.connect(self._transferCanceled)
        inThread.finished.connect(inThread.deleteLater)
        self._incoming[(peerID, transferID)] = inThread
        inThread.start()
        
        self.incomingTransferStarted.emit(peerID, transferID, targetDir, numFiles, size, name, inThread)
        
        answerDict = {u"id" : transferID,
                      u"port" : port}
        get_server().call("HELO_FT_ACK %s" % json.dumps(answerDict), peerIPs=[peerIP])
    
    def processAck(self, peerID, peerIP, value):
        self._processAck.emit(peerID, peerIP, value)
    @loggingSlot(object, object, object)
    def _processAckSlot(self, peerID, peerIP, value):
        try:
            answerDict = json.loads(value)
            
            if not type(answerDict) is dict:
                self.logger.warning(u"answerDict is no dict.")
                return
        except:
            self.logger.exception(u"Could not parse answer dict.")
            return
        
        if not u"id" in answerDict:
            self.logger.warning(u"answerDict does not contain transfer ID.")
            return
        if not u"port" in answerDict:
            self.logger.warning(u"answerDict does not contain port.")
            return
        
        transferID = answerDict[u"id"]
        port = answerDict[u"port"]
        outData = self._outgoing.get(transferID, None)
        
        if outData is None:
            self.logger.warning(u"Received ACK for transfer that I don't know of or that already timed out.")
            return
        elif type(outData) is DataSenderThread:
            self.logger.warning(u"Received ACK for transfer that is already running.")
            return
        
        targetID, paths, sendDict, _time = outData
        if targetID != peerID:
            self.logger.warning(u"Received ACK from peer that I wasn't sending to.")
            return
        
        outThread = DataSenderThread.send(peerIP, port, paths, sendDict, self.logger, parent=self)
        outThread.transferCanceled.connect(self._transferCanceled)
        outThread.errorOnTransfer.connect(self._removeUpload)
        outThread.successfullyTransferred.connect(self._removeUpload)
        outThread.finished.connect(outThread.deleteLater)
        outThread.setUserData((peerID, transferID))
        self._outgoing[transferID] = outThread
        outThread.start()
        self.outgoingTransferStarted.emit(transferID, outThread)
    
    def processCancel(self, peerID, value):
        self._processCancel.emit(peerID, value)
    @loggingSlot(object, object)
    def _processCancelSlot(self, peerID, value):
        try:
            cancelDict = json.loads(value)
            
            if not type(cancelDict) is dict:
                self.logger.warning(u"answerDict is no dict.")
                return
        except:
            self.logger.exception("Could not parse cancel dict.")
            return
        
        if not u"id" in cancelDict:
            self.logger.warning("Cancel dict does not contain transfer ID.")
            return
        
        if not u"up" in cancelDict:
            self.logger.warning("Cancel dict does not specify whether it was an upload or not.")
            return
        
        transferID = cancelDict[u"id"]
        wasUpload = cancelDict[u"up"]
        
        if wasUpload:
            # is download on this side
            thread = self._incoming.pop((peerID, transferID), None)
            if thread is None:
                self.logger.debug("Download canceled that was not running")
                return
        else:
            thread = self._outgoing.pop(transferID, None)
            if thread is None:
                self.logger.debug("Upload canceled that was not running")
                return
            
        thread.cancelTransfer()
    
    def sendFilesToPeer(self, toSend, peerID):
        self._sendFilesToPeer.emit(toSend, peerID)
    @loggingSlot(object, object)
    def _sendFilesToPeerSlot(self, toSend, peerID, transferID=None):
        if transferID is None:
            isRetry = False
            transferID = self._getNextID()
        else:
            isRetry = True
        
        if type(toSend) in (str, unicode):
            toSend = [toSend]
        elif type(toSend) is not list:
            self.logger.error("toSend must be path of list of paths")
            return
        
        # TODO separate preparation phase
        sendDict = DataSenderThread.prepareSending(toSend, compression=self._compression)
        self._outgoing[transferID] = (peerID, toSend, sendDict, time())
        
        self.startOutgoingTransfer.emit(transferID, peerID, toSend, self._downloadDir, sendDict[u"count"], sendDict[u"size"], isRetry)
        
        sendDict[u"id"] = transferID
        get_server().call("HELO_FT %s" % json.dumps(sendDict), peerIDs=[peerID])
        
    def downloadDirChanged(self, newDir):
        self._downloadDirChanged.emit(newDir)
    @loggingSlot(object)
    def _downloadDirChangedSlot(self, newDir):
        self._downloadDir = newDir
        
    def overwriteChanged(self, overwrite):
        self._overwriteChanged.emit(overwrite)
    @loggingSlot(bool)
    def _overwriteChangedSlot(self, overwrite):
        self._overwrite = overwrite
        
    def compressionChanged(self, comp):
        self._compressionChanged.emit(comp)
    @loggingSlot(object)
    def _compressionChangedSlot(self, comp):
        self._setCompression(comp)
        
    @loggingSlot(object, object, int)
    def retrySendFileToPeer(self, toSend, peerID, oldID):
        self._sendFilesToPeerSlot(toSend, peerID, oldID)
        
    @loggingSlot(int)
    def cancelOutgoingTransfer(self, transferID):
        data = self._outgoing.get(transferID, None)
        if type(data) is tuple:
            self._outgoing.pop(transferID, None)
            self.outgoingTransferCanceled.emit(transferID, False)