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)
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)
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())
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
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)