def download(self, sql): apiUrl = "http://{}.cartodb.com/api/v2/sql?api_key={}&format=spatialite&q={}".format( self.cartodbUser, self.apiKey, sql ) url = QUrl(apiUrl) request = self._getRequest(url) def finished(reply): tempdir = tempfile.tempdir if tempdir is None: tempdir = tempfile.mkdtemp() tf = tempfile.NamedTemporaryFile(delete=False) sqlite = QFile(tf.name) tf.close() if sqlite.open(QIODevice.WriteOnly): sqlite.write(reply.readAll()) sqlite.close() self.fetchContent.emit(tf.name) else: self.error.emit("Error saving downloaded file") manager = QNetworkAccessManager() manager.finished.connect(finished) reply = manager.get(request) loop = QEventLoop() reply.downloadProgress.connect(self.progressCB) reply.error.connect(self._error) reply.finished.connect(loop.exit) loop.exec_()
class FileCopier(QObject): """ Copies files from network """ finished = pyqtSignal() def __init__(self, file): super(FileCopier, self).__init__() self.file = QUrl("file:///{}".format(file)) self.manager = QNetworkAccessManager(self) self.connect(self.manager, SIGNAL("finished(QNetworkReply*)"), self.reply_finished) def reply_finished(self, reply): checkout_class = CheckoutStatusWindow() self.connect(reply, SIGNAL("downloadProgress(int, int)"), checkout_class.update_progress_bar) self.reply = reply checkout_class.progressBar.setMaximum(reply.size()) def run(self): """ Start the download :return: None """ self.manager.get(QNetworkRequest(self.file)) self.finished.emit()
class VersionChecker(QObject): def getRequestUrl(self, uuid, use_stats): base_url = "http://comictagger1.appspot.com/latest" args = "" if use_stats: if platform.system() == "Windows": plat = "win" elif platform.system() == "Linux": plat = "lin" elif platform.system() == "Darwin": plat = "mac" else: plat = "other" args = "?uuid={0}&platform={1}&version={2}".format( uuid, plat, ctversion.version) if not getattr(sys, 'frozen', None): args += "&src=T" return base_url + args def getLatestVersion(self, uuid, use_stats=True): try: resp = urllib2.urlopen(self.getRequestUrl(uuid, use_stats)) new_version = resp.read() except Exception as e: return None if new_version is None or new_version == "": return None return new_version.strip() versionRequestComplete = pyqtSignal(str) def asyncGetLatestVersion(self, uuid, use_stats): url = self.getRequestUrl(uuid, use_stats) self.nam = QNetworkAccessManager() self.nam.finished.connect(self.asyncGetLatestVersionComplete) self.nam.get(QNetworkRequest(QUrl(str(url)))) def asyncGetLatestVersionComplete(self, reply): if (reply.error() != QNetworkReply.NoError): return # read in the response new_version = str(reply.readAll()) if new_version is None or new_version == "": return self.versionRequestComplete.emit(new_version.strip())
class URLLabel(QLabel): """ QLabel subclass to support the display of images via URL. """ def __init__(self, parent): super(URLLabel, self).__init__(parent) self.connection = None self._original_pixmap = None self.connection = None def clear(self): self._original_pixmap = None super(URLLabel, self).clear() def setRemotePixmap(self, url): # Lazy creation of QNetworkManager, image download is asynchronous. # When the image download is complete (ie the "finished" signal), # the image data is read and drawn. if self.connection is None: self.connection = QNetworkAccessManager(self) self.connection.finished.connect(self.pixmapReceived) self.connection.get(QNetworkRequest(QUrl(url))) def pixmapReceived(self, reply): """ Slot for handling the return of the asynchronous image download. """ if reply.error() != QNetworkReply.NoError: reply.deleteLater() return data = reply.readAll() pixmap = QPixmap() pixmap.loadFromData(data) # The original image is stored as an attribute and drawn manually # using an overridden paintEvent. This is preferable to using the # setPixmap() functionality of QLabels because it allows more control # over the scaling of the pixmap and allows the size of the QLabel # to dictate the pixmap size, and not the other way around. self._original_pixmap = pixmap reply.deleteLater() self.update() def paintEvent(self, paintevent): super(URLLabel, self).paintEvent(paintevent) # Manually draw the downloaded pixmap, scaled to fit the size of the label. if self._original_pixmap: size = self.size() sized_pixmap = self._original_pixmap.scaled(size, Qt.KeepAspectRatio, Qt.SmoothTransformation) painter = QPainter(self) point = self.geometry().topLeft() painter.drawPixmap(point, sized_pixmap)
class SettingsProtocol(QObject): readSignal = pyqtSignal(bool, int, QString) def __init__(self, host, user, password): QObject.__init__(self) # Set the host self.host = host # Create a base64 encoded credential string from user name and password. # This is required for the HTTP basic access authentication, compare # also http://en.wikipedia.org/wiki/Basic_access_authentication self.userlogin = "******" % (user, password) self.login = "******" + QByteArray(self.userlogin).toBase64() # Create a new QNetworkAccessManager and connect it to the # authenticationRequired signal self.manager = QNetworkAccessManager(self) # self.connect(self.manager, SIGNAL("authenticationRequired( QNetworkReply*, QAuthenticator* )"), self.slotAuthenticationRequired) def read(self): self.connect(self.manager, SIGNAL("finished( QNetworkReply* )"), self.readRequestFinished) url = "%s/config/geometrytaggroups" % self.host qUrl = QUrl(url) self.request = QNetworkRequest(qUrl) self.request.setRawHeader("Authorization", self.login) self.request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json") self.manager.get(self.request) return url def readRequestFinished(self, reply): # Get the HTTP status code from the reply self.httpStatusCode = int(reply.attribute(QNetworkRequest.HttpStatusCodeAttribute).toString()) data = reply.readAll() # Check the status code see also http://en.wikipedia.org/wiki/HTTP_status_code # In case of a successful upload we get a 201 status code back and the # "stylePosted" signal is emitted with the first parameter set to True. # If the query didn't succeed, the status code is 4xx indicating that # the host was not found, the authentication failed or a forbidden # action in case a style with the same name already exists. It's up to # the receiver to handle these status codes. self.readSignal.emit(self.httpStatusCode in (200, 201), self.httpStatusCode, QString(data))
def cbUserData(self, data): if 'error' in data: # TODO Create image for error self.nameLB.setText( "<html><head/><body><p><span style=\" text-decoration: underline; color:red;\">error</span></p></body></html>" ) self.error.emit(data['error']) return self.currentUserData = data self.settings.setValue('/CartoDBPlugin/selected', self.currentUser) manager = QNetworkAccessManager() manager.finished.connect(self.returnAvatar) if 's3.amazonaws.com' in data['avatar_url']: imageUrl = QUrl(data['avatar_url']) else: imageUrl = QUrl('http:' + data['avatar_url']) request = QNetworkRequest(imageUrl) request.setRawHeader('User-Agent', 'QGIS 2.x') reply = manager.get(request) loop = QEventLoop() reply.finished.connect(loop.exit) loop.exec_()
def download(self, url, outfd=None): """ Download a given URL using current cookies. @param url: URL or path to download @param outfd: Output file-like stream. If None, return data string. @return: Bytes downloaded (None if something went wrong) @note: If url is a path, the current base URL will be pre-appended. """ def _on_reply(reply): url = unicode(reply.url().toString()) self._download_reply_status = not bool(reply.error()) self._download_reply_status = None if not urlparse.urlsplit(url).scheme: url = urlparse.urljoin(self.url, url) request = QNetworkRequest(QUrl(url)) # Create a new manager to process this download manager = QNetworkAccessManager() reply = manager.get(request) if reply.error(): raise SpynnerError("Download error: %s" % reply.errorString()) reply.downloaded_nbytes = 0 manager.setCookieJar(self.manager.cookieJar()) manager.connect(manager, SIGNAL('finished(QNetworkReply *)'), _on_reply) outfd_set = bool(outfd) if not outfd_set: outfd = StringIO() self._start_download(reply, outfd) while self._download_reply_status is None: self._events_loop() if outfd_set: return (reply.downloaded_nbytes if not reply.error() else None) else: return outfd.getvalue()
def download(self, sql): apiUrl = 'http://{}.{}/api/v2/sql?api_key={}&format=spatialite&q={}'.format( self.cartodbUser, self.hostname, self.apiKey, sql) url = QUrl(apiUrl) request = self._getRequest(url) def finished(reply): tempdir = tempfile.tempdir if tempdir is None: tempdir = tempfile.mkdtemp() tf = tempfile.NamedTemporaryFile(delete=False) sqlite = QFile(tf.name) tf.close() if (sqlite.open(QIODevice.WriteOnly)): sqlite.write(reply.readAll()) sqlite.close() self.fetchContent.emit(tf.name) else: self.error.emit('Error saving downloaded file') manager = QNetworkAccessManager() manager.finished.connect(finished) reply = manager.get(request) loop = QEventLoop() reply.downloadProgress.connect(self.progressCB) reply.error.connect(self._error) reply.finished.connect(loop.exit) loop.exec_()
class FileDownloader(QObject): def __init__(self, url, parent=None): QObject.__init__(self, parent) self.manager = QNetworkAccessManager() self._downloadedData = QByteArray() self.connect(self.manager, SIGNAL("finished(QNetworkReply*)"), self.fileDownloaded) request = QNetworkRequest(url) reply = self.manager.get(request) loop = QEventLoop() self.connect(reply, SIGNAL('finished()'), loop, SLOT('quit()')) loop.exec() #reply.finished.connect(self.fileDownloaded, reply) #self._downloadedData = reply.readAll() #print(self._downloadedData) def quit(self): print('QUIT') def fileDownloaded(self, reply): #print('fileDownloaded') self._downloadedData = reply.readAll() # emit a signal #print(self._downloadedData) reply.deleteLater() self.emit(SIGNAL("downloaded()")) def downloadedData(self): return self._downloadedData
class Test: def __init__(self): request = QNetworkRequest(QUrl("http://www.riverbankcomputing.co.uk/news")) self.manager = QNetworkAccessManager() self.manager.finished.connect(self.handleReply) self.manager.get(request) def handleReply(self, reply): print reply.readAll() print reply.error() QCoreApplication.quit()
def prepareInstallNanoCopy(self): #self.logMessage("installing") self.target="/instalador/nano" self.copying=True self.ui.WButons_2.setVisible(False) self.movieGreyIcon=QMovie(self.ui.icon_waitGrey) wantFormat=0 if self.ui.CHFormatNano.isChecked(): wantFormat=1 # for pipe commands use idCommand="/bin/bash" idParam="-c" idParam2="shell | piped command" arch=str(self.execShellProcess("uname", "-m").replace("\n","").replace("b",""))[1:] mem=str(self.execShellProcess("/bin/bash", "-c", "cat /proc/meminfo | grep MemTotal | awk ' { print $2 } '").replace("\n","").replace("b",""))[1:] persistentFilePath=str(self.target+"/persistent_/"+arch) if self.ui.CHChangesFile.isChecked(): persistentFileSize=str(self.realChangeFileSize).split(".")[0] else: persistentFileSize=0 self.installNanoKademarProcess=installNanoKademarProcess(self.target, self.selectedDeviceToInstall[0], wantFormat, self.pathInstaller, self.kademarType, persistentFilePath,persistentFileSize, self.totalSizeOfKademar) # FUNCIO COPIA self.connect(self.installNanoKademarProcess, SIGNAL("formated"), self.nanoEndedFormat) self.connect(self.installNanoKademarProcess, SIGNAL("endedCopy"), self.nanoEndedCopyProcess) self.connect(self.installNanoKademarProcess, SIGNAL("persistentFileCreated"), self.persistentFileCreated) self.connect(self.installNanoKademarProcess, SIGNAL("bootManagerInstalled"), self.bootManagerInstalled) #self.connect(self.installNanoKademarProcess, SIGNAL("progress"), self.updateProgressBar) if wantFormat: self.logMessage("Formating") self.installNanoKademarProcess.start() self.ui.iDisk.setPixmap(QPixmap(self.icon_greenTick)) self.movieGreyIcon.start() self.ui.iFormating.setMovie(self.movieGreyIcon) #Statistics if self.internet: from PyQt4.QtNetwork import QNetworkRequest,QNetworkAccessManager nwam = QNetworkAccessManager() request = QNetworkRequest (QUrl("http://www.kademar.org/UserCounter/count.php?login=&pc=&type=usb&kademar="+self.kademarType+"&arch="+arch+"&mem="+mem)) nwam.get(request)
def run(self): loop = QtCore.QEventLoop() nm = QNetworkAccessManager() reply = nm.get(QNetworkRequest(self.url)) reply.finished.connect(loop.quit) loop.exec_() data = reply.readAll() self.resourceLoaded.emit(self.res_type, self.url, data) reply.deleteLater() nm.deleteLater()
class Notifier(QThread): SERVER_URL = "http://127.0.0.1:6543" def __init__(self): QThread.__init__(self) # logging instance self.log = logging.getLogger('GDAIS.Notifier') # Network manager to send notifications self.manager = QNetworkAccessManager() self.manager.finished.connect(self._reply_finished) # Name of the equipment to notify (needs to be initialized before calling notify) self.equipment = '' # flag for when quit command is received self.exiting = False def quit(self): loop = QEventLoop() reply = self._make_request('quit') reply.finished.connect(loop.quit) loop.exec_() QThread.quit(self) def notify(self, event): self._make_request(event) def _make_request(self, event): url = QUrl("{0}/notify_{1}/{2}".format(self.SERVER_URL, event, self.equipment)) self.log.debug("Sending notification for '{0}' event to {1}".format( event, url.toString())) reply = self.manager.get(QNetworkRequest(url)) reply.error.connect(self._reply_error) return reply def _reply_finished(self, network_reply): self.log.debug("Reply received: {0}".format(network_reply.readAll())) def _reply_error(self, network_error): # TODO check if it is really an error self.log.error("Error receiving reply: {0}".format(network_error))
def __tryAgain(self): """ Private slot to retry the download. """ self.__tryAgainButton.setEnabled(False) self.__closeButton.setEnabled(False) self.__stopButton.setEnabled(True) if self.__page: nam = self.__page.networkAccessManager() else: nam = QNetworkAccessManager() reply = nam.get(QNetworkRequest(self.__url)) if self.__output.exists(): self.__output.remove() self.__reply = reply self.initialize()
class InfoBagDialog(QDialog, Ui_Info): def __init__(self, gebouwnummer, iface, parent=None): super(InfoBagDialog, self).__init__(parent) self.setupUi(self) self.setWindowTitle('BAG adressen') self.label.setText('Pand bevat de volgende adressen') self.actionButtonBox.button(QtGui.QDialogButtonBox.Close).setText("Sluiten") self.basewfs = "http://geo.zaanstad.nl/geoserver/wfs?request=GetFeature&version=2.0.0&outputFormat=JSON" self.iface = iface self.manager = QNetworkAccessManager(self) self.gebouwnummer = gebouwnummer self.getInfo(self.gebouwnummer) self.exec_() def getInfo(self, gebouwnummer): qurl = QUrl.fromUserInput(self.basewfs) qurl.addQueryItem('typeName', 'geo:bag_verblijfsobject') qurl.addQueryItem('filter', "<PropertyIsEqualTo><PropertyName>gebouwnummer</PropertyName><Literal>" + unicode(gebouwnummer) + "</Literal></PropertyIsEqualTo>") request = QNetworkRequest(qurl) reply = self.manager.get(request) reply.finished.connect(self.handleInfo) def handleInfo(self): reply = self.sender() error = reply.error() if error != QNetworkReply.NoError: self.iface.messageBar().pushMessage(reply.errorString(), level=QgsMessageBar.WARNING) reply.deleteLater() reply = None else: response_text = reply.readAll().data() data = json.loads(response_text) count = data['totalFeatures'] self.tableWidget.clear() self.tableWidget.setColumnCount(1); self.tableWidget.setRowCount(count); features = data['features'] for idx, feature in enumerate(features): text = QTableWidgetItem(feature['properties']['adres'] + ", " + feature['properties']['postcode'] + " (" + feature['properties']['gebruik'] + ")") self.tableWidget.setItem(idx,0,text) self.tableWidget.resizeColumnToContents(0)
def test_access_without_credentials(self): loop = QEventLoop() proxy = get_network_proxy() manager = QNetworkAccessManager() manager.setProxy(proxy) manager.finished.connect(loop.exit) reply = manager.get(QNetworkRequest(QUrl('http://aws.amazon.com/'))) loop.exec_(flags=QEventLoop.ExcludeUserInputEvents) if reply.isFinished(): self.assertEquals(self.server.log.getvalue(), '407 Proxy Authentication Required\n\n') else: if reply.isRunning(): self.failUnless(False, msg='The request has timed out.') else: self.failUnless(False, msg='A Network error occurred.')
def _request(self, url): ''' Request data from url @type url: str @rtype: QByteArray ''' nam = QNetworkAccessManager() reply = nam.get(QNetworkRequest(QUrl(url))) loop = QEventLoop() QObject.connect(reply, SIGNAL('finished()'), loop, SLOT('quit()')) loop.exec_() redirect = reply.attribute(QNetworkRequest.RedirectionTargetAttribute) # Follow redirects if redirect.type() == QVariant.Url: return self._request(unicode(redirect.toUrl().resolved(QUrl('')).toString())) return reply.readAll()
def cbUserData(self, data): self.currentUserData = data if self.toolbar.avatarImage is None: manager = QNetworkAccessManager() manager.finished.connect(self.returnAvatar) if 's3.amazonaws.com' in data['avatar_url']: imageUrl = QUrl(data['avatar_url']) else: imageUrl = QUrl('http:' + data['avatar_url']) request = QNetworkRequest(imageUrl) request.setRawHeader('User-Agent', 'QGIS 2.x') reply = manager.get(request) loop = QEventLoop() reply.finished.connect(loop.exit) loop.exec_() self.setUpUserData()
class Formaa(QObject): def __init__(self, parent=None): print "init" def proccess_finished(self, reply): print reply.header(QNetworkRequest.ContentTypeHeader).toString() QCoreApplication.quit() def readyRead(self, bytesReceived, bytesTotal): d = bytesReceived*100/bytesTotal print "bytesReceived: "+ str(d) + " bytesTotal: "+ str(bytesTotal) def download(self): self.manager = QNetworkAccessManager() QObject.connect(self.manager, SIGNAL("finished(QNetworkReply *)"),self.proccess_finished) self.request = QNetworkRequest(QUrl("http://hearablog.com/sites/a-smart-bear/post/taking-fail-fast-to-a-whole-nutha-level.mp3")) #self.request = QNetworkRequest(QUrl("http://www.google.com/")) self.request.setRawHeader('User-agent', 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US)') self.reply = self.manager.get(self.request) self.reply.downloadProgress.connect(self.readyRead)
def _fetch_inventory(self, url): cache_dir = config.cache_dir() cache_dir = os.path.join(cache_dir, "help", type(self).__qualname__) try: os.makedirs(cache_dir) except OSError: pass url = QUrl(self.inventory) if not url.isLocalFile(): # fetch and cache the inventory file. manager = QNetworkAccessManager(self) cache = QNetworkDiskCache() cache.setCacheDirectory(cache_dir) manager.setCache(cache) req = QNetworkRequest(url) self._reply = manager.get(req) manager.finished.connect(self._on_finished) else: self._load_inventory(open(str(url.toLocalFile()), "rb"))
def cbUserData(self, data): if 'error' in data: # TODO Create image for error self.nameLB.setText("<html><head/><body><p><span style=\" text-decoration: underline; color:red;\">error</span></p></body></html>") self.error.emit(data['error']) return self.currentUserData = data self.settings.setValue('/CartoDBPlugin/selected', self.currentUser) manager = QNetworkAccessManager() manager.finished.connect(self.returnAvatar) if 's3.amazonaws.com' in data['avatar_url']: imageUrl = QUrl(data['avatar_url']) else: imageUrl = QUrl('http:' + data['avatar_url']) request = QNetworkRequest(imageUrl) request.setRawHeader('User-Agent', 'QGIS 2.x') reply = manager.get(request) loop = QEventLoop() reply.finished.connect(loop.exit) loop.exec_()
def test_access_to_remote_succeeded(self): loop = QEventLoop() proxy = get_network_proxy() proxy.setUser(self.server.username) proxy.setPassword(self.server.password) manager = QNetworkAccessManager() manager.setProxy(proxy) manager.finished.connect(loop.exit) reply = manager.get(QNetworkRequest(QUrl('http://aws.amazon.com/'))) loop.exec_(flags=QEventLoop.ExcludeUserInputEvents) if reply.isFinished(): response_code = reply.attribute( QNetworkRequest.HttpStatusCodeAttribute).toString() self.assertEquals(response_code, '200') self.assertEquals(reply.url(), QUrl('http://aws.amazon.com/')) else: if reply.isRunning(): self.failUnless(False, msg='The request has timed out.') else: self.failUnless(False, msg='A Network error occurred.')
def _fetch_inventory(self): cache_dir = config.cache_dir() cache_dir = os.path.join(cache_dir, "help", "intersphinx") try: os.makedirs(cache_dir) except OSError: pass url = QUrl(self.inventory) if not self.islocal: # fetch and cache the inventory file manager = QNetworkAccessManager(self) cache = QNetworkDiskCache() cache.setCacheDirectory(cache_dir) manager.setCache(cache) req = QNetworkRequest(url) self._reply = manager.get(req) manager.finished.connect(self._on_finished) else: self._load_inventory(open(unicode(url.toLocalFile()), "rb"))
class BagadresString(QObject): def __init__(self): QObject.__init__(self) self.basewfs = "http://geo.zaanstad.nl/geoserver/wfs?request=GetFeature&version=2.0.0&outputFormat=JSON" self.manager = QNetworkAccessManager(self) self.timer = QTimer() self.loop = QEventLoop() self.reply = None def request(self, gebouwnummer): qurl = QUrl.fromUserInput(self.basewfs) qurl.addQueryItem('typeName', 'geo:bag_verblijfsobject') qurl.addQueryItem('filter', "<PropertyIsEqualTo><PropertyName>gebouwnummer</PropertyName><Literal>" + unicode(gebouwnummer) + "</Literal></PropertyIsEqualTo>") request = QNetworkRequest(qurl) self.reply = self.manager.get(request) self.reply.finished.connect(self.loop.quit) self.timer.start(5000) self.loop.exec_() if self.timer.isActive(): self.timer.stop() r = self.handleReply() else: raise Exception("Timeout error.") return r def handleReply(self): reply = self.reply response_text = reply.readAll().data() data = json.loads(response_text) feature = data['features'][0] reply.deleteLater() return feature['properties']['adres'] + " (" + feature['properties']['gebruik'] + ")"
class Browser(QApplication): """The browser application that comprises a parser, interpreter, renderer, and other core components of a browser. """ APPLICATION_NAME = 'Internet Exploder 164' def __init__(self, tmlParser, cs164Parser, interpreter, renderer): QApplication.__init__(self, [Browser.APPLICATION_NAME]) self.tmlParser = tmlParser self.cs164Parser = cs164Parser self.interpreter = interpreter self.renderer = renderer self.network = QNetworkAccessManager(self) self.window = Window() self.window.resize(610, 410) self.window.center() self.window.show() self.timers = {} self.networkReplies = [] self.focused = None def load(self, url): self.reset(self.window) connection = urlopen(url) try: dom = self.tmlParser.parse(connection.read()) self.renderer.render(dom, self.window.canvas) finally: connection.close() def createTimer(self): timer = QTimer() timer.start() self.timers[timer.timerId()] = timer print >> sys.stderr, 'Created new timer with ID', timer.timerId() return timer def createNetworkReply(self, uri): url = QUrl(uri, QUrl.TolerantMode) request = QNetworkRequest(url) reply = self.network.get(request) self.networkReplies.append(reply) return reply def setFocus(self, node): node['__qt'].setFocus() def clear(self, window): self.renderer.clear(window.canvas) def reset(self, window): self.clear(window) # Clear all of the timers. for timerId, timer in self.timers.items(): timer.stop() timer.timeout.disconnect() self.timers = {} # Close up all HTTP connections. for reply in self.networkReplies: reply.finished.disconnect() reply.abort() self.networkReplies = [] def relayout(self): self.execFun('relayout') def execScript(self, code, env={}): ast = self.cs164Parser.parse(code) self.interpreter.ExecGlobal(ast, env.copy()) def execFun(self, funName): self.interpreter.ExecFun(self.interpreter.globEnv[funName], []) @staticmethod def run(): """Launches the browser and returns its exit status code when closed. This method may never return; for example, if the OS immediately shuts down, further code may never run. """ # TODO: Perhaps run this in another thread. return QApplication.exec_()
class RestRequest(QObject): restApiUrl = '' # done = pyqtSignal(dict, name='done') def __init__(self, url, parentWindow, done, params=None): # parent not used super(RestRequest, self).__init__() # private self._manager = QNetworkAccessManager() if params is not None: url += '?' + '&'.join('{}={}'.format(k, urllib.quote(six.text_type(v).encode('utf8'))) for k, v in params.iteritems()) self.url = QUrl(RestRequest.restApiUrl + url) # connect asynchronous result, when a request finishes self._manager.finished.connect(self._finished) self._manager.sslErrors.connect(self._sslError) self._parentWindow = parentWindow self.done.connect(done, Qt.QueuedConnection) # private slot, no need to declare as slot @pyqtSlot(QNetworkReply) def _finished(self, reply): ''' Handle signal 'finished'. A network request has finished. ''' try: if reply.error() != QNetworkReply.NoError: raise Exception(reply.errorString()) data = six.text_type(reply.readAll()) data = json.loads(data) except Exception as e: data = { 'result': None, 'error': six.text_type(e) } self.done.emit(data) reply.deleteLater() # schedule for delete from main event loop @pyqtSlot(QNetworkReply, list) def _sslError(self, reply, errors): settings = QSettings() settings.beginGroup('ssl') cert = errors[0].certificate() digest = six.text_type(cert.digest().toHex()) approved = settings.value(digest, False).toBool() errorString = '<p>The certificate for <b>{}</b> has the following errors:</p><ul>'.format(cert.subjectInfo(QSslCertificate.CommonName)) for err in errors: errorString += '<li>' + err.errorString() + '</li>' errorString += '</ul>' if approved or QMessageBox.warning(self._parentWindow, 'SSL Warning', errorString, QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes: settings.setValue(digest, True) reply.ignoreSslErrors() settings.endGroup() def get(self): request = QNetworkRequest(self.url) request.setRawHeader('User-Agent', osDetector.getOs() + " - UDS Connector " + VERSION) self._manager.get(request)
class Nominatim(object): """Manage connexion to Nominatim.""" def __init__(self, url="http://nominatim.openstreetmap.org/search?format=json"): """ Constructor @param url:URL of Nominatim @type url:str """ self.__url = url self.network = QNetworkAccessManager() self.data = None self.network_reply = None self.loop = None def query(self, query): """ Perform a nominatim query @param query: Query to execute @type query: str @raise NetWorkErrorException @return: the result of the query @rtype: str """ url_query = QUrl(self.__url) query = QUrl.toPercentEncoding(query) url_query.addEncodedQueryItem('q', query) url_query.addQueryItem('info', 'QgisQuickOSMPlugin') url_query.setPort(80) proxy = get_proxy() if proxy: self.network.setProxy(proxy) request = QNetworkRequest(url_query) request.setRawHeader("User-Agent", "QuickOSM") self.network_reply = self.network.get(request) self.loop = QEventLoop() self.network.finished.connect(self._end_of_request) self.loop.exec_() if self.network_reply.error() == QNetworkReply.NoError: return json.loads(self.data) else: raise NetWorkErrorException(suffix="Nominatim API") def _end_of_request(self): self.data = self.network_reply.readAll().data().decode('utf-8') self.loop.quit() def get_first_polygon_from_query(self, query): """ Get first OSM_ID of a Nominatim area @param query: Query to execute @type query: str @raise NominatimAreaException: @return: First relation's osm_id @rtype: str """ data = self.query(query) for result in data: if result['osm_type'] == "relation": return result['osm_id'] # If no result has been return raise NominatimAreaException def get_first_point_from_query(self, query): """ Get first longitude, latitude of a Nominatim point @param query: Query to execute @type query: str @raise NominatimAreaException: @return: First relation's osm_id @rtype: str """ data = self.query(query) for result in data: if result['osm_type'] == "node": return result['lon'], result['lat'] # If no result has been return raise NominatimAreaException
class StravaUpload(QObject): LOGIN = '******' ATHLETES_SHOW = 'http://www.strava.com/api/v2/athletes/{id}' UPLOAD = 'http://www.strava.com/api/v2/upload' UPLOAD_STATUS = 'http://www.strava.com/api/v2/upload/status/{id}' statusMessage = pyqtSignal(str) totalProgress = pyqtSignal(int) itemProgress = pyqtSignal(int) authNeeded = pyqtSignal() finished = pyqtSignal() def __init__(self, tracks, auth_token=None, device_info=None, parent=None): super(StravaUpload, self).__init__(parent) self.device_info = device_info self.auth_token = auth_token self.tracks = tracks[:] self.current_track = None self.results = [] self.progress = 0 self.upload_id = None self._aborted = False self.reply = None self.network_manager = QNetworkAccessManager(self) def start(self): self._doAuthenticate() def cancel(self): self._aborted = True if self.reply is not None: self.reply.abort() def authenticate(self, username, password): log.debug('Sending auth request') req = QNetworkRequest(QUrl(self.LOGIN)) req.setHeader(QNetworkRequest.ContentTypeHeader, "application/x-www-form-urlencoded") self.reply = self.network_manager.post(req, urllib.urlencode({ 'email' : username, 'password' : password, })) self.reply.finished.connect(self._onAuthenticated) def _emitProgress(self, msg, value): self.progressString.emit(msg) self.progress.emit(value) def _doAuthenticate(self): self.statusMessage.emit('Authenticating') if self.auth_token is None: log.debug('Auth needed') self.authNeeded.emit() else: self._uploadNext() def _onAuthenticated(self): data = self._loadJson(str(self.reply.readAll())) self.reply = None if data is None: log.debug('Auth request failed (response: %s)', data) self._doAuthenticate() return if 'error' in data: log.debug('Auth request failed (response: %s)', data['error']) self._doAuthenticate() elif 'token' in data: log.debug('Auth OK') self.auth_token = data['token'] self._uploadNext() def _uploadNext(self): if self._aborted: return log.debug('Uploading next') self.itemProgress.emit(0) self.totalProgress.emit(self.progress) if not self.tracks: log.debug('Finished') self.finished.emit() return self.current_track = self.tracks.pop(0) self.progress += 1 self._doUpload() def _doUpload(self): if self.current_track is None or self._aborted: return track, track_type = self.current_track self.statusMessage.emit('Uploading {0}'.format(track['name'])) log.debug('Sending upload request (%s)', track['name']) # data_fields, data = bryton_gpx_to_strava_json(track['gpx']) data = tcx.bryton_gpx_to_tcx(track['gpx'], activity_type=track_type, pretty=False, device=self.device_info) req = QNetworkRequest(QUrl(self.UPLOAD)) req.setHeader(QNetworkRequest.ContentTypeHeader, "application/json") # d2 = json.loads(open('tmp/test.json').read()) # d2['token'] = self.auth_token # self.reply = self.network_manager.post(req, json.dumps(d2)) self.reply = self.network_manager.post(req, json.dumps({ 'token' : self.auth_token, 'type' : 'tcx', 'data' : data, 'activity_type' : track_type, })) self.reply.finished.connect(self._onUploaded) def _onUploaded(self): data = self._loadJson(str(self.reply.readAll())) self.reply = None if data is None: log.debug('Upload failed (response: %s)', data) self._uploadFailed('Unknown error') elif 'error' in data: log.debug('Upload failed (%s)', data['error']) self._uploadFailed(data['error']) else: log.debug('Upload OK (%s)', data['upload_id']) self.upload_id = data['upload_id'] self.statusMessage.emit('Checking upload status') QTimer.singleShot(2000, self._checkUpload) # self._progress += 1 # self.tracks.pop(0) # self.progress.emit(self._progress) # self.upload() def _uploadFailed(self, msg): self.results.append({ 'status' : 'ERROR', 'msg' : 'Failed: ' + msg, }) self._uploadNext() def _uploadOk(self): self.results.append({ 'status' : 'OK', 'msg': 'Successfully uploaded' }) self._uploadNext() def _checkUpload(self): if self._aborted: return log.debug('Sending upload status request (%s)', self.upload_id) url = QUrl(self.UPLOAD_STATUS.format(id=self.upload_id)) url.addQueryItem('token', self.auth_token) req = QNetworkRequest(url) self.reply = self.network_manager.get(req) self.reply.finished.connect(self._onUploadStatus) def _onUploadStatus(self): data = self._loadJson(str(self.reply.readAll())) self.reply = None if data is None: log.debug('Upload status failed (response: %s)', data) self._uploadFailed('Unknown error') elif 'upload_error' in data: log.debug('Upload status failed (%s)', data['upload_error']) self.statusMessage.emit('Upload failed') self._uploadFailed(data['upload_error']) else: self.statusMessage.emit(data['upload_status']) log.debug('Upload status %d (%s)', self.upload_id, data['upload_progress']) progress = int(data['upload_progress']) if progress == 0: progress = 10 # Just add a little to the progress so it doesn't look stuck self.itemProgress.emit(progress) if progress == 100: self._uploadOk() else: QTimer.singleShot(2500, self._checkUpload) def _loadJson(self, data): try: return json.loads(data) except ValueError, e: return None
class LobbyWidget(FormClass, BaseClass): planetClicked = QtCore.pyqtSignal(int) hovering = QtCore.pyqtSignal() creditsUpdated = QtCore.pyqtSignal(int) rankUpdated = QtCore.pyqtSignal(int) creditsUpdated = QtCore.pyqtSignal(int) victoriesUpdated = QtCore.pyqtSignal(int) attacksUpdated = QtCore.pyqtSignal() depotUpdated = QtCore.pyqtSignal() planetUpdated = QtCore.pyqtSignal(int) attackProposalUpdated = QtCore.pyqtSignal(int) ReinforcementUpdated = QtCore.pyqtSignal(dict) planetaryDefenseUpdated = QtCore.pyqtSignal(dict) ReinforcementsGroupUpdated = QtCore.pyqtSignal(dict) ReinforcementsGroupDeleted = QtCore.pyqtSignal(dict) dominationUpdated = QtCore.pyqtSignal(int) playersListUpdated = QtCore.pyqtSignal(dict) teamUpdated = QtCore.pyqtSignal(dict) searchingUpdated = QtCore.pyqtSignal(bool) def __init__(self, client, *args, **kwargs): logger.debug("Lobby instantiating.") BaseClass.__init__(self, *args, **kwargs) self.setupUi(self) self.client = client #self.client.galacticwarTab.setStyleSheet(util.readstylesheet("galacticwar/galacticwar.css")) self.COLOR_FACTIONS = {} self.mapTransparency = 10 self.AA = True self.rotation = True self.stars = 25 self.GWOptions = GWOptions(self) self.GWOptions.loadSettings() self.client.galacticwarTab.layout().addWidget(self) self.downloader = QNetworkAccessManager(self) self.downloader.finished.connect(self.finishRequest) self.shaderlist = [] self.texturelist = {} self.shaders = {} self.infoPanel = None self.OGLdisplay = None self.galaxy = Galaxy(self) self.channel = None self.initDone = False self.uid = None self.faction = None self.name = None self.rank = None self.credits = 0 self.victories = 0 self.enslavedBy = None self.attacks = {} self.state = ClientState.NONE ## Network initialization self.socket = QtNetwork.QTcpSocket() self.socket.readyRead.connect(self.readFromServer) self.socket.disconnected.connect(self.disconnectedFromServer) self.socket.error.connect(self.socketError) self.blockSize = 0 self.progress = QtGui.QProgressDialog() self.progress.setMinimum(0) self.progress.setMaximum(0) # def focusEvent(self, event): # return BaseClass.focusEvent(self, event) def showEvent(self, event): if self.state != ClientState.ACCEPTED: fa.exe.check("gw") if self.doConnect(): logger.info("connection done") self.doLogin() else: if not self.initDone: logger.info("init not done") self.doLogin() else: if self.faction == None: logger.info("not faction") self.doLogin() return BaseClass.showEvent(self, event) def updateOptions(self): ''' settings galactic wars options''' self.GWOptions.show() def createChannel(self, chat, name): self.channel = gwChannel(chat, name, True) def finishRequest(self, reply): filename = reply.url().toString().rsplit('/', 1)[1] root, _ = os.path.splitext(filename) toFile = os.path.join(GW_TEXTURE_DIR, filename) writeFile = QtCore.QFile(toFile) if (writeFile.open(QtCore.QIODevice.WriteOnly)): writeFile.write(reply.readAll()) writeFile.close() else: logger.warn("%s is not writeable in in %s. Skipping." % (filename, GW_TEXTURE_DIR)) if root in self.texturelist: del self.texturelist[root] if len(self.texturelist) == 0: self.setup() self.progress.close() def doConnect(self): logger.debug("Connecting to server") if self.client.state == ClientState.ACCEPTED: self.progress.setCancelButtonText("Cancel") self.progress.setWindowFlags(QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowTitleHint) self.progress.setAutoClose(False) self.progress.setAutoReset(False) self.progress.setModal(1) self.progress.setWindowTitle("Galactic War Network...") self.progress.setLabelText("Gating in ...") self.progress.show() # self.login = self.client.login.strip() # logger.info("Attempting to gate as: " + str(self.client.login)) self.state = ClientState.NONE # Begin connecting. self.socket.connectToHost(LOBBY_HOST, LOBBY_PORT) while (self.socket.state() != QtNetwork.QAbstractSocket.ConnectedState ) and self.progress.isVisible(): QtGui.QApplication.processEvents() # #Perform Version Check first if not self.socket.state( ) == QtNetwork.QAbstractSocket.ConnectedState: self.progress.close() # in case it was still showing... # We either cancelled or had a TCP error, meaning the connection failed.. if self.progress.wasCanceled(): logger.warn("doConnect() aborted by user.") else: logger.error("doConnect() failed with clientstate " + str(self.state) + ", socket errorstring: " + self.socket.errorString()) return False else: return True def doLogin(self): ''' login in the GW server We are using the main login and session to check legitimity of the client. ''' self.progress.setLabelText("Gating in...") self.progress.reset() self.progress.show() logger.info("Attempting to gate as: " + str(self.client.login)) self.state = ClientState.NONE self.send( dict(command="hello", version=util.VERSION_STRING, port=self.client.gamePort, login=self.client.login, session=self.client.session)) while (not self.state) and self.progress.isVisible(): QtGui.QApplication.processEvents() if self.progress.wasCanceled(): logger.warn("Gating aborted by user.") return False self.progress.close() if self.state == ClientState.ACCEPTED: logger.info("Gating accepted.") self.progress.close() self.client.actionGalacticWar.triggered.connect(self.updateOptions) self.client.actionGalacticWar.setEnabled(True) return True #self.connected.emit() elif self.state == ClientState.REJECTED: logger.warning("Gating rejected.") return False else: # A more profound error has occurrect (cancellation or disconnection) return False def setup(self): self.galaxy.computeVoronoi() from glDisplay import GLWidget from infopanel import InfoPanelWidget from newsTicker import NewsTicker from reinforcements import PlanetaryWidget from reinforcements import ReinforcementWidget #items panels self.teams = Teams(self) self.planetaryItems = PlanetaryWidget(self) self.reinforcementItems = ReinforcementWidget(self) self.OGLdisplay = GLWidget(self) self.newsTicker = NewsTicker(self) self.galaxyLayout.addWidget(self.OGLdisplay) self.galaxyLayout.addWidget(self.newsTicker) self.newsTicker.setMaximumHeight(20) self.infoPanel = InfoPanelWidget(self) self.info_Panel.layout().addWidget(self.infoPanel) self.send(dict(command="init_done", status=True)) self.infoPanel.setup() def get_rank(self, faction, rank): return RANKS[faction][rank] def handle_welcome(self, message): self.state = ClientState.ACCEPTED def handle_planetary_defense_info(self, message): '''populate planetary defense lists''' self.planetaryDefenseUpdated.emit(message) def handle_group_reinforcements_info(self, message): '''populate current group reinforcements ''' self.ReinforcementsGroupUpdated.emit(message) def handle_group_reinforcements_deleted(self, message): self.ReinforcementsGroupDeleted.emit(message) def handle_reinforcement_item_info(self, message): '''populate reinforcement lists''' self.ReinforcementUpdated.emit(message) def handle_resource_required(self, message): if message["action"] == "shaders": self.shaderlist = message["data"] self.send(dict(command="request", action="shaders")) elif message["action"] == "textures": for tex in message["data"]: if not tex in self.texturelist: self.texturelist[tex] = message["data"][tex] def handle_shader(self, message): name = message["name"] shader_fragment = message["shader_fragment"] shader_vertex = message["shader_vertex"] if not name in self.shaders: self.shaders[name] = {} self.shaders[name]["fragment"] = shader_fragment self.shaders[name]["vertex"] = shader_vertex if name in self.shaderlist: self.shaderlist.remove(name) self.check_resources() #we have all our shader. def get_texture_name(self, tex): return os.path.join(GW_TEXTURE_DIR, tex + ".png") def download_textures(self): self.progress.show() self.progress.setLabelText("Downloading resources ...") textInCache = [] for tex in self.texturelist: if os.path.exists(self.get_texture_name(tex)): if util.md5( self.get_texture_name(tex)) == self.texturelist[tex]: #logger.debug(tex + ".png in cache.") textInCache.append(tex) continue #logger.debug("Downloading " + tex + ".png") self.downloader.get( QNetworkRequest(QtCore.QUrl(TEXTURE_SERVER + tex + ".png"))) for tex in textInCache: del self.texturelist[tex] if len(self.texturelist) == 0: self.progress.close() self.setup() def check_resources(self): '''checking if we have everything we need''' if len(self.shaderlist) == 0 and self.initDone: self.download_textures() def handle_remove_team(self, message): self.teamUpdated.emit(dict(leader=None, members=[])) def handle_team(self, message): self.teamUpdated.emit(message) def handle_request_team(self, message): ''' We have a team invitation from someone ''' who = message["who"] uid = message["uid"] self.infoPanel.formTeam() self.infoPanel.teamwidget.addProposal(who, uid) def handle_news_feed(self, message): '''Adding news to news feed''' if hasattr(self, "newsTicker"): if self.newsTicker: self.newsTicker.addNews(message["news"]) def handle_player_info(self, message): ''' Update Player stats ''' self.uid = int(message["uid"]) self.faction = message["faction"] self.name = message["name"] self.rank = message["rank"] self.credits = message["credits"] self.victories = message["victories"] logger.debug("Received player info : victories %i, credits %i" % (self.victories, self.credits)) self.rankUpdated.emit(self.rank) self.creditsUpdated.emit(self.credits) self.victoriesUpdated.emit(self.victories) def handle_game_upgrades(self, message): '''writing reinforcement list''' upgrades = message["upgrades"] fa.gwgametable.writeTable(upgrades, "gwReinforcementList.gw") # and we empty the unit reinforcement list self.reinforcementItems.reset() def handle_domination(self, message): master = message["master"] self.enslavedBy = master self.dominationUpdated.emit(master) def handle_attack_result(self, message): self.progress.close() result = message["result"] if result == "won": QtGui.QMessageBox.information(self, "War report", "You win !", QtGui.QMessageBox.Close) def handle_attack_proposal(self, message): planetuid = message["planetuid"] self.attackProposalUpdated.emit(planetuid) def handle_attacks_info(self, message): if self.OGLdisplay == None: return attacks = message["attacks"] self.attacks = {} for playeruid in attacks: playeruid_int = int(playeruid) if not playeruid_int in self.attacks: self.attacks[playeruid_int] = {} for planetuid in attacks[playeruid]: planetuid_int = int(planetuid) self.attacks[playeruid_int][planetuid_int] = attacks[ playeruid][planetuid] self.attacksUpdated.emit() def handle_planet_defense_remove(self, message): '''handling removing defenses for a planet''' planetuid = message["planetuid"] self.galaxy.removeDefenses(planetuid) def handle_planet_depot_info(self, message): '''handling depots''' planetuid = message["planetuid"] self.galaxy.updateDepot(planetuid, message) self.depotUpdated.emit() def handle_planet_defense_info(self, message): '''handling defenses for planets''' planetuid = message["planetuid"] self.galaxy.updateDefenses(planetuid, message) def handle_planet_info(self, message): uid = message['uid'] if not uid in self.galaxy.control_points: display = message['visible'] x = message['posx'] y = message['posy'] size = message['size'] textureMd5 = message['md5tex'] name = message['name'] desc = message['desc'] sector = message['sector'] if display: texture = message['texture'] mapname = message['mapname'] maxplayer = message['maxplayer'] if not texture in self.texturelist: self.texturelist[texture] = textureMd5 else: mapname = "" texture = 0 maxplayer = 0 self.galaxy.addPlanet(uid, sector, name, desc, x, y, size, maxplayer=maxplayer, mapname=mapname, texture=texture, init=True, display=display) self.galaxy.update(message) if not uid in self.galaxy.links: self.galaxy.links[uid] = message['links'] else: self.galaxy.update(message) self.planetUpdated.emit(message['sector']) def handle_logged_in(self, message): self.handle_player_info(message) if self.faction != None: self.client.galacticwarTab.setStyleSheet( util.readstylesheet("galacticwar/galacticwar.css").replace( "%FACTION%", FACTIONS[self.faction])) self.attacksUpdated.emit() def handle_create_account(self, message): if message["action"] == 0: accountCreator = loginwizards.gwSelectFaction(self) accountCreator.exec_() if self.faction != None: self.send( dict(command="account_creation", action=0, faction=self.faction)) else: self.client.mainTabs.setCurrentIndex(0) QtGui.QMessageBox.warning( self, "No faction :(", "You need to pledge allegiance to a faction in order to play Galactic War !" ) elif message["action"] == 1: name = message["name"] self.faction = message["faction"] self.rank = message["rank"] question = QtGui.QMessageBox.question( self, "Avatar name generation", "Your avatar name will be : <br><br>" + self.get_rank(self.faction, self.rank) + " " + name + ".<br><br>Press Reset to generate another, Ok to accept.", QtGui.QMessageBox.Reset, QtGui.QMessageBox.Ok) if question == QtGui.QMessageBox.Reset: self.send(dict(command="account_creation", action=1)) else: self.name = name self.send(dict(command="account_creation", action=2)) def handle_faction_player_list(self, message): '''players online''' self.playersListUpdated.emit(message["players"]) def handle_init_done(self, message): if message['status'] == True: self.initDone = True self.check_resources() def handle_social(self, message): if "autojoin" in message: if message["autojoin"] == 0: self.client.autoJoin.emit(["#UEF"]) elif message["autojoin"] == 1: self.client.autoJoin.emit(["#Aeon"]) elif message["autojoin"] == 2: self.client.autoJoin.emit(["#Cybran"]) elif message["autojoin"] == 3: self.client.autoJoin.emit(["#Seraphim"]) def handle_searching(self, message): state = message["state"] if state == "on": self.searchingUpdated.emit(True) else: self.searchingUpdated.emit(False) def handle_notice(self, message): self.client.handle_notice(message) def handle_update(self, message): update = message["update"] if not util.developer(): logger.warn("Server says that Updating is needed.") self.progress.close() self.state = ClientState.OUTDATED fa.updater.fetchClientUpdate(update) def process(self, action, stream): if action == "PING": self.writeToServer("PONG") else: self.dispatchJSON(action, stream) def dispatchJSON(self, data_string, stream): ''' A fairly pythonic way to process received strings as JSON messages. ''' message = json.loads(data_string) cmd = "handle_" + message['command'] #logger.debug("Incoming JSON Command: " + data_string) if hasattr(self, cmd): getattr(self, cmd)(message) else: logger.error("command unknown : %s", cmd) def send(self, message): data = json.dumps(message) logger.info("Outgoing JSON Message: " + data) self.writeToServer(data) @QtCore.pyqtSlot() def readFromServer(self): ins = QtCore.QDataStream(self.socket) ins.setVersion(QtCore.QDataStream.Qt_4_2) while ins.atEnd() == False: if self.blockSize == 0: if self.socket.bytesAvailable() < 4: return self.blockSize = ins.readUInt32() if self.socket.bytesAvailable() < self.blockSize: return action = ins.readQString() self.process(action, ins) self.blockSize = 0 @QtCore.pyqtSlot() def disconnectedFromServer(self): logger.warn("Disconnected from lobby server.") if self.state == ClientState.ACCEPTED: QtGui.QMessageBox.warning( QtGui.QApplication.activeWindow(), "Disconnected from Galactic War", "The lobby lost the connection to the Galactic War server.<br/><b>You might still be able to chat.<br/>To play, try reconnecting a little later!</b>", QtGui.QMessageBox.Close) self.initDone = False self.client.mainTabs.setCurrentIndex(0) self.client.mainTabs.setTabEnabled( self.client.mainTabs.indexOf(self.client.galacticwarTab), False) self.client.mainTabs.setTabText( self.client.mainTabs.indexOf(self.client.galacticwarTab), "offline") self.state = ClientState.DROPPED def writeToServer(self, action, *args, **kw): ''' This method is the workhorse of the client, and is used to send messages, queries and commands to the server. ''' logger.debug("Client: " + action) block = QtCore.QByteArray() out = QtCore.QDataStream(block, QtCore.QIODevice.ReadWrite) out.setVersion(QtCore.QDataStream.Qt_4_2) out.writeUInt32(0) out.writeQString(action) for arg in args: if type(arg) is IntType: out.writeInt(arg) elif isinstance(arg, basestring): out.writeQString(arg) elif type(arg) is FloatType: out.writeFloat(arg) elif type(arg) is ListType: out.writeQVariantList(arg) elif type(arg) is DictType: out.writeQString(json.dumps(arg)) elif type(arg) is QtCore.QFile: arg.open(QtCore.QIODevice.ReadOnly) fileDatas = QtCore.QByteArray(arg.readAll()) #seems that that logger doesn't work #logger.debug("file size ", int(fileDatas.size())) out.writeInt(fileDatas.size()) out.writeRawData(fileDatas) # This may take a while. We display the progress bar so the user get a feedback self.sendFile = True self.progress.setLabelText("Sending file to server") self.progress.setCancelButton(None) self.progress.setWindowFlags(QtCore.Qt.CustomizeWindowHint | QtCore.Qt.WindowTitleHint) self.progress.setAutoClose(True) self.progress.setMinimum(0) self.progress.setMaximum(100) self.progress.setModal(1) self.progress.setWindowTitle("Uploading in progress") self.progress.show() arg.close() else: logger.warn("Uninterpreted Data Type: " + str(type(arg)) + " sent as str: " + str(arg)) out.writeQString(str(arg)) out.device().seek(0) out.writeUInt32(block.size() - 4) self.bytesToSend = block.size() - 4 self.socket.write(block) @QtCore.pyqtSlot(QtNetwork.QAbstractSocket.SocketError) def socketError(self, error): logger.error("TCP Socket Error: " + self.socket.errorString()) if self.state > ClientState.NONE: # Positive client states deserve user notification. QtGui.QMessageBox.critical( None, "TCP Error", "A TCP Connection Error has occurred:<br/><br/><b>" + self.socket.errorString() + "</b>", QtGui.QMessageBox.Close)
class MikiEdit(QTextEdit): def __init__(self, parent=None): super(MikiEdit, self).__init__(parent) self.parent = parent self.settings = parent.settings self.setFontPointSize(12) self.setVisible(False) self.ix = open_dir(self.settings.indexdir) # Spell checker support try: import enchant enchant.Dict() self.speller = enchant.Dict() except ImportError: print("Spell checking unavailable. Need to install pyenchant.") self.speller = None except enchant.errors.DictNotFoundError: print("Spell checking unavailable. Need to install dictionary (e.g. aspell-en).") self.speller = None self.imageFilter = "" self.documentFilter = "" for ext in self.settings.attachmentImage: self.imageFilter += " *" + ext for ext in self.settings.attachmentDocument: self.documentFilter += " *" + ext self.imageFilter = "Image (" + self.imageFilter.strip() + ")" self.documentFilter = "Document (" + self.documentFilter.strip() + ")" self.downloadAs = "" self.networkManager = QNetworkAccessManager() self.networkManager.finished.connect(self.downloadFinished) def updateIndex(self): ''' Update whoosh index, which cost much computing resource ''' page = self.parent.notesTree.currentPage() content = self.toPlainText() try: #writer = self.ix.writer() writer = AsyncWriter(self.ix) if METADATA_CHECKER.match(content) and 'meta' in self.settings.extensions: no_metadata_content = METADATA_CHECKER.sub("", content, count=1).lstrip() self.settings.md.reset().convert(content) writer.update_document( path=page, title=parseTitle(content, page), content=no_metadata_content, tags=','.join(self.settings.md.Meta.get('tags', [])).strip()) writer.commit() else: writer.update_document( path=page, title=parseTitle(content, page), content=content, tags='') writer.commit() except: print("Whoosh commit failed.") def downloadFinished(self, reply): if reply.error(): print("Failed to download") else: attFile = QFile(self.downloadAs) attFile.open(QIODevice.WriteOnly) attFile.write(reply.readAll()) attFile.close() print("Succeeded") reply.deleteLater() def mimeFromText(self, text): mime = QMimeData() mime.setText(text) return mime def createMimeDataFromSelection(self): """ Reimplement this to prevent copied text taken as hasHtml() """ plaintext = self.textCursor().selectedText() # From QTextCursor doc: # if the selection obtained from an editor spans a line break, # the text will contain a Unicode U+2029 paragraph separator character # instead of a newline \n character text = plaintext.replace('\u2029', '\n') return self.mimeFromText(text) def insertFromMimeData(self, source): """ Intended behavior If copy/drag something that hasUrls, then check the extension name: if image then apply image pattern ![Alt text](/path/to/img.jpg) else apply link pattern [text](http://example.net) If copy/drag something that hasImage, then ask for file name If copy/drag something that hasHtml, then html2text Else use the default insertFromMimeData implementation """ item = self.parent.notesTree.currentItem() attDir = self.parent.notesTree.itemToAttachmentDir(item) if not QDir(attDir).exists(): QDir().mkpath(attDir) if source.hasUrls(): for qurl in source.urls(): url = qurl.toString() filename, extension = os.path.splitext(url) filename = os.path.basename(filename) newFilePath = os.path.join(attDir, filename + extension).replace(os.sep, '/') relativeFilePath = newFilePath.replace(self.settings.notebookPath, "..") attachments = self.settings.attachmentImage + self.settings.attachmentDocument if QUrl(qurl).isLocalFile(): if extension.lower() in attachments: nurl = url.replace("file://", "") QFile.copy(nurl, newFilePath) self.parent.updateAttachmentView() if extension.lower() in self.settings.attachmentImage: text = "![%s](%s)" % (filename, relativeFilePath) elif extension.lower() in self.settings.attachmentDocument: text = "[%s%s](%s)\n" % (filename, extension, relativeFilePath) else: text = "[%s%s](%s)\n" % (filename, extension, url) else: if extension.lower() in attachments: self.downloadAs = newFilePath self.networkManager.get(QNetworkRequest(qurl)) if extension.lower() in self.settings.attachmentImage: text = "![%s](%s)" % (filename, relativeFilePath) elif extension.lower() in self.settings.attachmentDocument: text = "[%s%s](%s)\n" % (filename, extension, relativeFilePath) else: text = "[%s%s](%s)\n" % (filename, extension, url) super(MikiEdit, self).insertFromMimeData(self.mimeFromText(text)) elif source.hasImage(): img = source.imageData() attDir = self.parent.notesTree.itemToAttachmentDir(item) dialog = LineEditDialog(attDir, self) if dialog.exec_(): fileName = dialog.editor.text() if not QFileInfo(fileName).suffix(): fileName += '.jpg' filePath = os.path.join(attDir, fileName).replace(os.sep, '/') img.save(filePath) relativeFilePath = filePath.replace(self.settings.notebookPath, "..") text = "![%s](%s)" % (fileName, relativeFilePath) super(MikiEdit, self).insertFromMimeData(self.mimeFromText(text)) elif source.hasHtml(): html = source.html() if HAS_HTML2TEXT: backToMarkdown = html2text.HTML2Text() markdown = backToMarkdown.handle(html) super(MikiEdit, self).insertFromMimeData(self.mimeFromText(markdown)) else: super(MikiEdit, self).insertFromMimeData(self.mimeFromText(html)) else: super(MikiEdit, self).insertFromMimeData(source) def insertAttachment(self, filePath, fileType): item = self.parent.notesTree.currentItem() attDir = self.parent.notesTree.itemToAttachmentDir(item) filename, extension = os.path.splitext(filePath) filename = os.path.basename(filename) newFilePath = os.path.join(attDir, filename + extension).replace(os.sep, '/') relativeFilePath = newFilePath.replace(self.settings.notebookPath, "..") if not os.path.exists(attDir): os.makedirs(attDir) QFile.copy(filePath, newFilePath) self.parent.updateAttachmentView() if fileType == self.imageFilter: text = "![%s](%s)" % (filename, relativeFilePath) else: text = "[%s%s](%s)\n" % (filename, extension, relativeFilePath) self.insertPlainText(text) def insertAttachmentWrapper(self): (filePath, fileType) = QFileDialog.getOpenFileNameAndFilter( self, self.tr('Insert attachment'), '', self.imageFilter + ";;" + self.documentFilter) if filePath == "": return self.insertAttachment(filePath, fileType) def contextMenuEvent(self, event): def correctWord(cursor, word): # From QTextCursor doc: # if there is a selection, the selection is deleted and replaced return lambda: cursor.insertText(word) popup_menu = self.createStandardContextMenu() # Spellcheck the word under mouse cursor, not self.textCursor cursor = self.cursorForPosition(event.pos()) cursor.select(QTextCursor.WordUnderCursor) text = cursor.selectedText() if self.speller and text: if not self.speller.check(text): lastAction = popup_menu.actions()[0] for word in self.speller.suggest(text)[:10]: action = QAction(word, popup_menu) action.triggered.connect(correctWord(cursor, word)) action.setFont(QFont("sans", weight=QFont.Bold)) popup_menu.insertAction(lastAction, action) popup_menu.insertSeparator(lastAction) popup_menu.exec_(event.globalPos()) def keyPressEvent(self, event): """ for Qt.Key_Tab, expand as 4 spaces for other keys, use default implementation """ if event.key() == Qt.Key_Tab: self.insertPlainText(' ') else: QTextEdit.keyPressEvent(self, event) ''' def closeEvent(self, event): self.ix.close() print('closed idx') event.accept() ''' def save(self, item): pageName = self.parent.notesTree.itemToPage(item) filePath = self.parent.notesTree.itemToFile(item) htmlFile = self.parent.notesTree.itemToHtmlFile(item) fh = QFile(filePath) try: if not fh.open(QIODevice.WriteOnly): raise IOError(fh.errorString()) except IOError as e: QMessageBox.warning(self, 'Save Error', 'Failed to save %s: %s' % (pageName, e)) raise finally: if fh is not None: savestream = QTextStream(fh) savestream << self.toPlainText() fh.close() self.document().setModified(False) # Fork a process to update index, which benefit responsiveness. Thread(target=self.updateIndex).start() def toHtml(self): '''markdown.Markdown.convert v.s. markdown.markdown ~~Previously `convert` was used, but it doens't work with fenced_code~~ fixed that by calling markdown.Markdown.reset before each conversion ''' htmltext = self.toPlainText() if 'asciimathml' in self.settings.extensions: stuff=JSCRIPT_TPL.format(self.settings.mathjax) else: stuff='' return self.settings.md.reset().convert(htmltext)+stuff # md = markdown.Markdown(extensions) # return md.convert(htmltext) def saveAsHtml(self, htmlFile = None): """ Save as Complete (with css and images) or HTML Only To be merged with saveNoteAs """ if not htmlFile: (htmlFile, htmlType) = QFileDialog.getSaveFileNameAndFilter( self, self.tr("Export to HTML"), "", "Complete;;HTML Only") if htmlFile == '': return if not QFileInfo(htmlFile).suffix(): htmlFile += '.html' if htmlType == "Complete": self.saveCompleteHtml(htmlFile) else: self.saveHtmlOnly(htmlFile) def saveCompleteHtml(self, htmlFile): html = QFile(htmlFile) html.open(QIODevice.WriteOnly) savestream = QTextStream(html) css = QFile(self.settings.cssfile) css.open(QIODevice.ReadOnly) # Use a html lib may be a better idea? savestream << "<html><head><meta charset='utf-8'></head>" # Css is inlined. savestream << "<style>" savestream << QTextStream(css).readAll() savestream << "</style>" # Note content savestream << self.toHtml() savestream << "</html>" html.close() def saveHtmlOnly(self, htmlFile): fileDir = os.path.dirname(htmlFile) QDir().mkpath(fileDir) html = QFile(htmlFile) html.open(QIODevice.WriteOnly) savestream = QTextStream(html) savestream << """ <html><head> <meta charset="utf-8"> <link rel="stylesheet" href="/css/notebook.css" type="text/css" /> </head> """ # Note content savestream << self.toHtml() savestream << "</html>" html.close()
class downloadManager(QtCore.QObject): ''' This class allows downloading stuff in the background''' def __init__(self, parent=None): self.client = parent self.nam = QNetworkAccessManager() self.nam.finished.connect(self.finishedDownload) self.modRequests = {} self.mapRequests = {} self.mapRequestsItem = [] def finishedDownload(self, reply): ''' finishing downloads ''' urlstring = reply.url().toString() reqlist = [] if urlstring in self.mapRequests: reqlist = self.mapRequests[urlstring] if urlstring in self.modRequests: reqlist = self.modRequests[urlstring] if reqlist: #save the map from cache name = os.path.basename(reply.url().toString()) pathimg = os.path.join(util.CACHE_DIR, name) img = QtCore.QFile(pathimg) img.open(QtCore.QIODevice.WriteOnly) img.write(reply.readAll()) img.close() if os.path.exists(pathimg): #Create alpha-mapped preview image try: pass # the server already sends 100x100 pic # img = QtGui.QImage(pathimg).scaled(100,100) # img.save(pathimg) except: pathimg = "games/unknown_map.png" logger.info("Failed to resize " + name) else: pathimg = "games/unknown_map.png" logger.debug("Web Preview failed for: " + name) logger.debug("Web Preview used for: " + name) for requester in reqlist: if requester: if requester in self.mapRequestsItem: requester.setIcon(0, util.icon(pathimg, False)) self.mapRequestsItem.remove(requester) else: requester.setIcon(util.icon(pathimg, False)) if urlstring in self.mapRequests: del self.mapRequests[urlstring] if urlstring in self.modRequests: del self.modRequests[urlstring] def downloadMap(self, name, requester, item=False): ''' Downloads a preview image from the web for the given map name ''' #This is done so generated previews always have a lower case name. This doesn't solve the underlying problem (case folding Windows vs. Unix vs. FAF) name = name.lower() if len(name) == 0: return url = QtCore.QUrl(VAULT_PREVIEW_ROOT + urllib2.quote(name) + ".png") if not url.toString() in self.mapRequests: logger.debug("Searching map preview for: " + name) self.mapRequests[url.toString()] = [] request = QNetworkRequest(url) self.nam.get(request) self.mapRequests[url.toString()].append(requester) else: self.mapRequests[url.toString()].append(requester) if item: self.mapRequestsItem.append(requester) def downloadModPreview(self, strurl, requester): url = QtCore.QUrl(strurl) if not url.toString() in self.modRequests: logger.debug("Searching mod preview for: " + os.path.basename(strurl).rsplit('.', 1)[0]) self.modRequests[url.toString()] = [] request = QNetworkRequest(url) self.nam.get(request) self.modRequests[url.toString()].append(requester)
class OnlineUpdater(QWidgetComponentFactory(uiFile=COMPONENT_UI_FILE)): """ | Defines the :mod:`sibl_gui.components.addons.onlineUpdater.onlineUpdater` Component Interface class. | This Component provides online updating capabilities to the Application available through options exposed in the :mod:`sibl_gui.components.core.preferencesManager.preferencesManager` Component ui. """ def __init__(self, parent=None, name=None, *args, **kwargs): """ Initializes the class. :param parent: Object parent. :type parent: QObject :param name: Component name. :type name: unicode :param \*args: Arguments. :type \*args: \* :param \*\*kwargs: Keywords arguments. :type \*\*kwargs: \*\* """ LOGGER.debug("> Initializing '{0}()' class.".format(self.__class__.__name__)) super(OnlineUpdater, self).__init__(parent, name, *args, **kwargs) # --- Setting class attributes. --- self.deactivatable = True self.__engine = None self.__settings = None self.__settingsSection = None self.__preferencesManager = None self.__templatesOutliner = None self.__locationsBrowser = None self.__ioDirectory = "remote/" self.__repositoryUrl = REPOSITORY_URL self.__releasesFileUrl = "sIBL_GUI_Releases.rc" self.__networkAccessManager = None self.__releasesFileReply = None self.__remoteUpdater = None self.__reportUpdateStatus = None #****************************************************************************************************************** #*** Attributes properties. #****************************************************************************************************************** @property def engine(self): """ Property for **self.__engine** attribute. :return: self.__engine. :rtype: QObject """ return self.__engine @engine.setter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def engine(self, value): """ Setter for **self.__engine** attribute. :param value: Attribute value. :type value: QObject """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "engine")) @engine.deleter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def engine(self): """ Deleter for **self.__engine** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "engine")) @property def settings(self): """ Property for **self.__settings** attribute. :return: self.__settings. :rtype: QSettings """ return self.__settings @settings.setter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def settings(self, value): """ Setter for **self.__settings** attribute. :param value: Attribute value. :type value: QSettings """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "settings")) @settings.deleter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def settings(self): """ Deleter for **self.__settings** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "settings")) @property def settingsSection(self): """ Property for **self.__settingsSection** attribute. :return: self.__settingsSection. :rtype: unicode """ return self.__settingsSection @settingsSection.setter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def settingsSection(self, value): """ Setter for **self.__settingsSection** attribute. :param value: Attribute value. :type value: unicode """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "settingsSection")) @settingsSection.deleter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def settingsSection(self): """ Deleter for **self.__settingsSection** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "settingsSection")) @property def preferencesManager(self): """ Property for **self.__preferencesManager** attribute. :return: self.__preferencesManager. :rtype: QWidget """ return self.__preferencesManager @preferencesManager.setter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def preferencesManager(self, value): """ Setter for **self.__preferencesManager** attribute. :param value: Attribute value. :type value: QWidget """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "preferencesManager")) @preferencesManager.deleter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def preferencesManager(self): """ Deleter for **self.__preferencesManager** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "preferencesManager")) @property def templatesOutliner(self): """ Property for **self.__templatesOutliner** attribute. :return: self.__templatesOutliner. :rtype: QWidget """ return self.__templatesOutliner @templatesOutliner.setter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def templatesOutliner(self, value): """ Setter for **self.__templatesOutliner** attribute. :param value: Attribute value. :type value: QWidget """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "templatesOutliner")) @templatesOutliner.deleter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def templatesOutliner(self): """ Deleter for **self.__templatesOutliner** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "templatesOutliner")) @property def locationsBrowser(self): """ Property for **self.__locationsBrowser** attribute. :return: self.__locationsBrowser. :rtype: QWidget """ return self.__locationsBrowser @locationsBrowser.setter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def locationsBrowser(self, value): """ Setter for **self.__locationsBrowser** attribute. :param value: Attribute value. :type value: QWidget """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "locationsBrowser")) @locationsBrowser.deleter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def locationsBrowser(self): """ Deleter for **self.__locationsBrowser** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "locationsBrowser")) @property def ioDirectory(self): """ Property for **self.__ioDirectory** attribute. :return: self.__ioDirectory. :rtype: unicode """ return self.__ioDirectory @ioDirectory.setter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def ioDirectory(self, value): """ Setter for **self.__ioDirectory** attribute. :param value: Attribute value. :type value: unicode """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "ioDirectory")) @ioDirectory.deleter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def ioDirectory(self): """ Deleter for **self.__ioDirectory** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "ioDirectory")) @property def repositoryUrl(self): """ Property for **self.__repositoryUrl** attribute. :return: self.__repositoryUrl. :rtype: unicode """ return self.__repositoryUrl @repositoryUrl.setter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def repositoryUrl(self, value): """ Setter for **self.__repositoryUrl** attribute. :param value: Attribute value. :type value: unicode """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "repositoryUrl")) @repositoryUrl.deleter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def repositoryUrl(self): """ Deleter for **self.__repositoryUrl** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "repositoryUrl")) @property def releasesFileUrl(self): """ Property for **self.__releasesFileUrl** attribute. :return: self.__releasesFileUrl. :rtype: unicode """ return self.__releasesFileUrl @releasesFileUrl.setter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def releasesFileUrl(self, value): """ Setter for **self.__releasesFileUrl** attribute. :param value: Attribute value. :type value: unicode """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "releasesFileUrl")) @releasesFileUrl.deleter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def releasesFileUrl(self): """ Deleter for **self.__releasesFileUrl** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "releasesFileUrl")) @property def networkAccessManager(self): """ Property for **self.__networkAccessManager** attribute. :return: self.__networkAccessManager. :rtype: QNetworkAccessManager """ return self.__networkAccessManager @networkAccessManager.setter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def networkAccessManager(self, value): """ Setter for **self.__networkAccessManager** attribute. :param value: Attribute value. :type value: QNetworkAccessManager """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "networkAccessManager")) @networkAccessManager.deleter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def networkAccessManager(self): """ Deleter for **self.__networkAccessManager** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "networkAccessManager")) @property def releaseReply(self): """ Property for **self.__releasesFileReply** attribute. :return: self.__releasesFileReply. :rtype: QNetworkReply """ return self.__releasesFileReply @releaseReply.setter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def releaseReply(self, value): """ Setter for **self.__releasesFileReply** attribute. :param value: Attribute value. :type value: QNetworkReply """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "releaseReply")) @releaseReply.deleter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def releaseReply(self): """ Deleter for **self.__releasesFileReply** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "releaseReply")) @property def remoteUpdater(self): """ Property for **self.__remoteUpdater** attribute. :return: self.__remoteUpdater. :rtype: object """ return self.__remoteUpdater @remoteUpdater.setter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def remoteUpdater(self, value): """ Setter for **self.__remoteUpdater** attribute. :param value: Attribute value. :type value: object """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "remoteUpdater")) @remoteUpdater.deleter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def remoteUpdater(self): """ Deleter for **self.__remoteUpdater** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "remoteUpdater")) @property def reportUpdateStatus(self): """ Property for **self.__reportUpdateStatus** attribute. :return: self.__reportUpdateStatus. :rtype: bool """ return self.__reportUpdateStatus @reportUpdateStatus.setter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def reportUpdateStatus(self, value): """ Setter for **self.__reportUpdateStatus** attribute. :param value: Attribute value. :type value: bool """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format(self.__class__.__name__, "reportUpdateStatus")) @reportUpdateStatus.deleter @foundations.exceptions.handleExceptions(foundations.exceptions.ProgrammingError) def reportUpdateStatus(self): """ Deleter for **self.__reportUpdateStatus** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format(self.__class__.__name__, "reportUpdateStatus")) #****************************************************************************************************************** #*** Class methods. #****************************************************************************************************************** def activate(self, engine): """ Activates the Component. :param engine: Engine to attach the Component to. :type engine: QObject :return: Method success. :rtype: bool """ LOGGER.debug("> Activating '{0}' Component.".format(self.__class__.__name__)) self.__engine = engine self.__settings = self.__engine.settings self.__settingsSection = self.name self.__preferencesManager = self.__engine.componentsManager["factory.preferencesManager"] self.__templatesOutliner = self.__engine.componentsManager["core.templatesOutliner"] self.__locationsBrowser = self.__engine.componentsManager["addons.locationsBrowser"] self.__ioDirectory = os.path.join(self.__engine.userApplicationDataDirectory, Constants.ioDirectory, self.__ioDirectory) not foundations.common.pathExists(self.__ioDirectory) and os.makedirs(self.__ioDirectory) self.__networkAccessManager = QNetworkAccessManager() self.__reportUpdateStatus = True self.activated = True return True def deactivate(self): """ Deactivates the Component. :return: Method success. :rtype: bool """ LOGGER.debug("> Deactivating '{0}' Component.".format(self.__class__.__name__)) self.__engine = None self.__settings = None self.__settingsSection = None self.__preferencesManager = None self.__templatesOutliner = None self.__locationsBrowser = None self.__ioDirectory = os.path.basename(os.path.abspath(self.__ioDirectory)) self.__networkAccessManager = None self.__reportUpdateStatus = None self.activated = False return True def initializeUi(self): """ Initializes the Component ui. :return: Method success. :rtype: bool """ LOGGER.debug("> Initializing '{0}' Component ui.".format(self.__class__.__name__)) self.__engine.parameters.deactivateWorkerThreads and \ LOGGER.info( "{0} | 'OnStartup' Online Updater worker thread deactivated by '{1}' command line parameter value!".format( self.__class__.__name__, "deactivateWorkerThreads")) self.__Check_For_New_Releases_On_Startup_checkBox_setUi() self.__Ignore_Non_Existing_Templates_checkBox_setUi() # Signals / Slots. self.Check_For_New_Releases_pushButton.clicked.connect(self.__Check_For_New_Releases_pushButton__clicked) self.Check_For_New_Releases_On_Startup_checkBox.stateChanged.connect( self.__Check_For_New_Releases_On_Startup_checkBox__stateChanged) self.Ignore_Non_Existing_Templates_checkBox.stateChanged.connect( self.__Ignore_Non_Existing_Templates_checkBox__stateChanged) self.initializedUi = True return True def uninitializeUi(self): """ Uninitializes the Component ui. :return: Method success. :rtype: bool """ LOGGER.debug("> Uninitializing '{0}' Component ui.".format(self.__class__.__name__)) # Signals / Slots. self.Check_For_New_Releases_pushButton.clicked.disconnect(self.__Check_For_New_Releases_pushButton__clicked) self.Check_For_New_Releases_On_Startup_checkBox.stateChanged.disconnect( self.__Check_For_New_Releases_On_Startup_checkBox__stateChanged) self.Ignore_Non_Existing_Templates_checkBox.stateChanged.disconnect( self.__Ignore_Non_Existing_Templates_checkBox__stateChanged) self.initializedUi = False return True def onStartup(self): """ Defines the slot triggered on Framework startup. :return: Method success. :rtype: bool """ LOGGER.debug("> Calling '{0}' Component Framework 'onStartup' method.".format(self.__class__.__name__)) self.__reportUpdateStatus = False if not self.__engine.parameters.deactivateWorkerThreads and \ self.Check_For_New_Releases_On_Startup_checkBox.isChecked(): self.checkForNewReleases() return True def addWidget(self): """ Adds the Component Widget to the engine. :return: Method success. :rtype: bool """ LOGGER.debug("> Adding '{0}' Component Widget.".format(self.__class__.__name__)) self.__preferencesManager.Others_Preferences_gridLayout.addWidget(self.Online_Updater_groupBox) def removeWidget(self): """ Removes the Component Widget from the engine. :return: Method success. :rtype: bool """ LOGGER.debug("> Removing '{0}' Component Widget.".format(self.__class__.__name__)) self.Online_Updater_groupBox.setParent(None) def __Check_For_New_Releases_On_Startup_checkBox_setUi(self): """ Sets the **Check_For_New_Releases_On_Startup_checkBox** Widget. """ # Adding settings key if it doesn't exists. self.__settings.getKey(self.__settingsSection, "checkForNewReleasesOnStartup").isNull() and \ self.__settings.setKey(self.__settingsSection, "checkForNewReleasesOnStartup", Qt.Checked) checkForNewReleasesOnStartup = self.__settings.getKey(self.__settingsSection, "checkForNewReleasesOnStartup") LOGGER.debug("> Setting '{0}' with value '{1}'.".format("Check_For_New_Releases_On_Startup_checkBox", foundations.common.getFirstItem(checkForNewReleasesOnStartup.toInt()))) self.Check_For_New_Releases_On_Startup_checkBox.setCheckState( foundations.common.getFirstItem(checkForNewReleasesOnStartup.toInt())) def __Check_For_New_Releases_On_Startup_checkBox__stateChanged(self, state): """ Defines the slot triggered by **Check_For_New_Releases_On_Startup_checkBox** Widget when state changed. :param state: Checkbox state. :type state: int """ LOGGER.debug("> Check for new releases on startup state: '{0}'.".format(state)) self.__settings.setKey(self.__settingsSection, "checkForNewReleasesOnStartup", state) def __Ignore_Non_Existing_Templates_checkBox_setUi(self): """ Sets the **Ignore_Non_Existing_Templates_checkBox** Widget. """ # Adding settings key if it doesn't exists. self.__settings.getKey(self.__settingsSection, "ignoreNonExistingTemplates").isNull() and \ self.__settings.setKey(self.__settingsSection, "ignoreNonExistingTemplates", Qt.Checked) ignoreNonExistingTemplates = self.__settings.getKey(self.__settingsSection, "ignoreNonExistingTemplates") LOGGER.debug("> Setting '{0}' with value '{1}'.".format("Ignore_Non_Existing_Templates_checkBox", foundations.common.getFirstItem(ignoreNonExistingTemplates.toInt()))) self.Ignore_Non_Existing_Templates_checkBox.setCheckState( foundations.common.getFirstItem(ignoreNonExistingTemplates.toInt())) def __Ignore_Non_Existing_Templates_checkBox__stateChanged(self, state): """ Defines the slot triggered by **Ignore_Non_Existing_Templates_checkBox** Widget when state changed. :param state: Checkbox state. :type state: int """ LOGGER.debug("> Ignore non existing Templates state: '{0}'.".format(state)) self.__settings.setKey(self.__settingsSection, "ignoreNonExistingTemplates", state) def __Check_For_New_Releases_pushButton__clicked(self, checked): """ Defines the slot triggered by **Check_For_New_Releases_pushButton** Widget when clicked. :param checked: Checked state. :type checked: bool """ self.checkForNewReleasesUi() @foundations.exceptions.handleExceptions(sibl_gui.exceptions.NetworkError) def __releasesFileReply__finished(self): """ Defines the slot triggered by the releases file reply when finished. """ self.__engine.stopProcessing() if not self.__releasesFileReply.error(): content = [] while not self.__releasesFileReply.atEnd (): content.append(foundations.strings.toString(self.__releasesFileReply.readLine())) LOGGER.debug("> Parsing releases file content.") sectionsFileParser = SectionsFileParser() sectionsFileParser.content = content sectionsFileParser.parse() releases = {} for remoteObject in sectionsFileParser.sections: if remoteObject != Constants.applicationName: databaseTemplates = \ sibl_gui.components.core.database.operations.filterTemplates("^{0}$".format(remoteObject), "name") databaseTemplate = foundations.common.getFirstItem([foundations.common.getFirstItem(databaseTemplate) for databaseTemplate in sorted(((databaseTemplate, databaseTemplate.release) for databaseTemplate in databaseTemplates), reverse=True, key=lambda x:(foundations.strings.getVersionRank(x[1])))]) if not self.__engine.parameters.databaseReadOnly: if databaseTemplate: if databaseTemplate.release != sectionsFileParser.getValue("Release", remoteObject): releases[remoteObject] = ReleaseObject(name=remoteObject, repositoryVersion=sectionsFileParser.getValue( "Release", remoteObject), localVersion=databaseTemplate.release, type=sectionsFileParser.getValue("Type", remoteObject), url=sectionsFileParser.getValue("Url", remoteObject), comment=sectionsFileParser.getValue("Comment", remoteObject)) else: if not self.Ignore_Non_Existing_Templates_checkBox.isChecked(): releases[remoteObject] = ReleaseObject(name=remoteObject, repositoryVersion=sectionsFileParser.getValue( "Release", remoteObject), localVersion=None, type=sectionsFileParser.getValue("Type", remoteObject), url=sectionsFileParser.getValue("Url", remoteObject), comment=sectionsFileParser.getValue("Comment", remoteObject)) else: LOGGER.info("{0} | '{1}' repository remote object skipped by '{2}' command line parameter value!".format( self.__class__.__name__, remoteObject, "databaseReadOnly")) else: if Constants.version != sectionsFileParser.getValue("Release", remoteObject): releases[remoteObject] = ReleaseObject(name=remoteObject, repositoryVersion=sectionsFileParser.getValue("Release", remoteObject), localVersion=Constants.version, url=sectionsFileParser.getValue("Url", remoteObject), type=sectionsFileParser.getValue("Type", remoteObject), comment=None) if releases: LOGGER.debug("> Initializing Remote Updater.") self.__remoteUpdater = RemoteUpdater(self, releases, Qt.Window) self.__remoteUpdater.show() else: self.__reportUpdateStatus and self.__engine.notificationsManager.notify( "{0} | '{1}' is up to date!".format(self.__class__.__name__, Constants.applicationName)) else: raise sibl_gui.exceptions.NetworkError("{0} | QNetworkAccessManager error code: '{1}'.".format( self.__class__.__name__, self.__releasesFileReply.error())) def __getReleasesFile(self, url): """ Gets the releases file. """ LOGGER.debug("> Downloading '{0}' releases file.".format(url.path())) self.__engine.startProcessing("Retrieving Releases File ...") self.__releasesFileReply = self.__networkAccessManager.get(QNetworkRequest(url)) self.__releasesFileReply.finished.connect(self.__releasesFileReply__finished) @foundations.exceptions.handleExceptions(umbra.exceptions.notifyExceptionHandler, sibl_gui.exceptions.NetworkError, Exception) def checkForNewReleasesUi(self): """ Checks for new releases. :return: Method success. :rtype: bool :note: May require user interaction. """ if not self.__networkAccessManager.networkAccessible(): raise sibl_gui.exceptions.NetworkError("{0} | Network is not accessible!".format(self.__class__.__name__)) self.__reportUpdateStatus = True if self.checkForNewReleases(): return True else: raise Exception("{0} | Exception raised while checking for new releases!".format(self.__class__.__name__)) @foundations.exceptions.handleExceptions(sibl_gui.exceptions.NetworkError, Exception) def checkForNewReleases(self): """ Checks for new releases. :return: Method success. :rtype: bool """ if not self.__networkAccessManager.networkAccessible(): raise sibl_gui.exceptions.NetworkError("{0} | Network is not accessible!".format(self.__class__.__name__)) self.__getReleasesFile(QUrl(os.path.join(self.__repositoryUrl, self.__releasesFileUrl))) return True
class AboutDialog(QDialog): """Dialog for showing info about TortoiseHg""" def __init__(self, parent=None): super(AboutDialog, self).__init__(parent) self.setWindowIcon(qtlib.geticon('thg_logo')) self.setWindowTitle(_('About')) self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) self.vbox = QVBoxLayout() self.vbox.setSpacing(8) self.logo_lbl = QLabel() self.logo_lbl.setMinimumSize(QSize(92, 50)) self.logo_lbl.setScaledContents(False) self.logo_lbl.setAlignment(Qt.AlignCenter) thglogofile = paths.get_tortoise_icon('thg_logo_92x50.png') self.logo_lbl.setPixmap(QPixmap(thglogofile)) self.vbox.addWidget(self.logo_lbl) self.name_version_libs_lbl = QLabel() self.name_version_libs_lbl.setText(' ') self.name_version_libs_lbl.setAlignment(Qt.AlignCenter) self.name_version_libs_lbl.setTextInteractionFlags( Qt.TextSelectableByMouse) self.vbox.addWidget(self.name_version_libs_lbl) self.getVersionInfo() self.copyright_lbl = QLabel() self.copyright_lbl.setAlignment(Qt.AlignCenter) self.copyright_lbl.setText( '\n' + _('Copyright 2008-2012 Steve Borho and others')) self.vbox.addWidget(self.copyright_lbl) self.courtesy_lbl = QLabel() self.courtesy_lbl.setAlignment(Qt.AlignCenter) self.courtesy_lbl.setText( _('Several icons are courtesy of the TortoiseSVN project') + '\n') self.vbox.addWidget(self.courtesy_lbl) self.download_url_lbl = QLabel() self.download_url_lbl.setAlignment(Qt.AlignCenter) self.download_url_lbl.setMouseTracking(True) self.download_url_lbl.setAlignment(Qt.AlignCenter) self.download_url_lbl.setTextInteractionFlags( Qt.LinksAccessibleByMouse) self.download_url_lbl.setOpenExternalLinks(True) self.download_url_lbl.setText( '<a href=%s>%s</a>' % ('http://tortoisehg.org', _('You can visit our site here'))) self.vbox.addWidget(self.download_url_lbl) # Let's have some space between the url and the buttons. self.blancline_lbl = QLabel() self.vbox.addWidget(self.blancline_lbl) self.hbox = QHBoxLayout() self.license_btn = QPushButton() self.license_btn.setText(_('&License')) self.license_btn.setAutoDefault(False) self.license_btn.clicked.connect(self.showLicense) self.hspacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.close_btn = QPushButton() self.close_btn.setText(_('&Close')) self.close_btn.setDefault(True) self.close_btn.clicked.connect(self.close) self.hbox.addWidget(self.license_btn) self.hbox.addItem(self.hspacer) self.hbox.addWidget(self.close_btn) self.vbox.addLayout(self.hbox) self.setLayout(self.vbox) self.layout().setSizeConstraint(QLayout.SetFixedSize) self._readsettings() # Spawn it later, so that the dialog gets visible quickly. QTimer.singleShot(0, self.getUpdateInfo) self._newverreply = None def getVersionInfo(self): def make_version(tuple): vers = ".".join([str(x) for x in tuple]) return vers thgv = (_('version %s') % version.version()) libv = (_('with Mercurial-%s, Python-%s, PyQt-%s, Qt-%s') % \ (hglib.hgversion, make_version(sys.version_info[0:3]), PYQT_VERSION_STR, QT_VERSION_STR)) par = ('<p style=\" margin-top:0px; margin-bottom:6px;\">' '<span style=\"font-size:%spt; font-weight:600;\">' '%s</span></p>') name = (par % (14, 'TortoiseHg')) thgv = (par % (10, thgv)) nvl = ''.join([name, thgv, libv]) self.name_version_libs_lbl.setText(nvl) @pyqtSlot() def getUpdateInfo(self): verurl = 'http://tortoisehg.bitbucket.org/curversion.txt' # If we use QNetworkAcessManager elsewhere, it should be shared # through the application. self._netmanager = QNetworkAccessManager(self) self._newverreply = self._netmanager.get(QNetworkRequest(QUrl(verurl))) self._newverreply.finished.connect(self.uFinished) @pyqtSlot() def uFinished(self): newver = (0, 0, 0) try: f = self._newverreply.readAll().data().splitlines() self._newverreply.close() self._newverreply = None newver = tuple([int(p) for p in f[0].split('.')]) upgradeurl = f[1] # generic download URL platform = sys.platform if platform == 'win32': from win32process import IsWow64Process as IsX64 platform = IsX64() and 'x64' or 'x86' # linux2 for Linux, darwin for OSX for line in f[2:]: p, _url = line.split(':', 1) if platform == p: upgradeurl = _url.strip() break except (IndexError, ImportError, ValueError): pass try: thgv = version.version() if '+' in thgv: thgv = thgv[:thgv.index('+')] curver = tuple([int(p) for p in thgv.split('.')]) except ValueError: curver = (0, 0, 0) if newver > curver: url_lbl = _('A new version of TortoiseHg is ready for download!') urldata = ('<a href=%s>%s</a>' % (upgradeurl, url_lbl)) self.download_url_lbl.setText(urldata) def showLicense(self): from tortoisehg.hgqt import license ld = license.LicenseDialog(self) ld.show() def closeEvent(self, event): if self._newverreply: self._newverreply.abort() self._writesettings() super(AboutDialog, self).closeEvent(event) def _readsettings(self): s = QSettings() self.restoreGeometry(s.value('about/geom').toByteArray()) def _writesettings(self): s = QSettings() s.setValue('about/geom', self.saveGeometry())
class WebClient(BaseWebClient): """A webclient with a qtnetwork backend.""" proxy_instance = None def __init__(self, *args, **kwargs): """Initialize this instance.""" super(WebClient, self).__init__(*args, **kwargs) self.nam = QNetworkAccessManager(QCoreApplication.instance()) self.nam.finished.connect(self._handle_finished) self.nam.authenticationRequired.connect(self._handle_authentication) self.nam.proxyAuthenticationRequired.connect(self.handle_proxy_auth) self.nam.sslErrors.connect(self._handle_ssl_errors) self.replies = {} self.proxy_retry = False self.setup_proxy() # Force Qt to load the system certificates QSslSocket.setDefaultCaCertificates(QSslSocket.systemCaCertificates()) # Apply our local certificates as the SSL configuration to be used # for all QNetworkRequest calls. self.ssl_config = QSslConfiguration.defaultConfiguration() ca_certs = self.ssl_config.caCertificates() try: for path in glob.glob( os.path.join(get_cert_dir(), "UbuntuOne*.pem")): with open(path) as f: cert = QSslCertificate(f.read()) if cert.isValid(): ca_certs.append(cert) else: logger.error("invalid certificate: {}".format(path)) except (IndexError, IOError) as err: raise WebClientError( "Unable to configure SSL certificates: {}".format(err)) self.ssl_config.setCaCertificates(ca_certs) def _set_proxy(self, proxy): """Set the proxy to be used.""" QNetworkProxy.setApplicationProxy(proxy) self.nam.setProxy(proxy) def setup_proxy(self): """Setup the proxy settings if needed.""" # QtNetwork knows how to use the system settings on both Win and Mac if sys.platform.startswith("linux"): settings = gsettings.get_proxy_settings() enabled = len(settings) > 0 if enabled and WebClient.proxy_instance is None: proxy = build_proxy(settings) self._set_proxy(proxy) WebClient.proxy_instance = proxy elif enabled and WebClient.proxy_instance: logger.info("Proxy already in use.") else: logger.info("Proxy is disabled.") else: if WebClient.proxy_instance is None: logger.info("Querying OS for proxy.") QNetworkProxyFactory.setUseSystemConfiguration(True) def handle_proxy_auth(self, proxy, authenticator): """Proxy authentication is required.""" logger.info("auth_required %r, %r", self.proxy_username, proxy.hostName()) if (self.proxy_username is not None and self.proxy_username != str(authenticator.user())): authenticator.setUser(self.proxy_username) WebClient.proxy_instance.setUser(self.proxy_username) if (self.proxy_password is not None and self.proxy_password != str(authenticator.password())): authenticator.setPassword(self.proxy_password) WebClient.proxy_instance.setPassword(self.proxy_password) def _perform_request(self, request, method, post_buffer): """Return a deferred that will be fired with a Response object.""" d = defer.Deferred() if method == "GET": reply = self.nam.get(request) elif method == "HEAD": reply = self.nam.head(request) else: reply = self.nam.sendCustomRequest(request, method, post_buffer) self.replies[reply] = d return d @defer.inlineCallbacks def request(self, iri, method="GET", extra_headers=None, oauth_credentials=None, post_content=None): """Return a deferred that will be fired with a Response object.""" uri = self.iri_to_uri(iri) request = QNetworkRequest(QUrl(uri)) request.setSslConfiguration(self.ssl_config) headers = yield self.build_request_headers(uri, method, extra_headers, oauth_credentials) for key, value in headers.items(): request.setRawHeader(key, value) post_buffer = QBuffer() post_buffer.setData(post_content) try: result = yield self._perform_request(request, method, post_buffer) except ProxyUnauthorizedError as e: app_proxy = QNetworkProxy.applicationProxy() proxy_host = app_proxy.hostName() if app_proxy else "proxy server" got_creds = yield self.request_proxy_auth_credentials( proxy_host, self.proxy_retry) if got_creds: self.proxy_retry = True result = yield self.request(iri, method, extra_headers, oauth_credentials, post_content) else: excp = WebClientError('Proxy creds needed.', e) defer.returnValue(excp) defer.returnValue(result) def _handle_authentication(self, reply, authenticator): """The reply needs authentication.""" if authenticator.user() != self.username: authenticator.setUser(self.username) if authenticator.password() != self.password: authenticator.setPassword(self.password) def _handle_finished(self, reply): """The reply has finished processing.""" assert reply in self.replies d = self.replies.pop(reply) error = reply.error() content = reply.readAll() if not error: headers = HeaderDict() for key, value in reply.rawHeaderPairs(): headers[str(key)].append(str(value)) response = Response(bytes(content), headers) d.callback(response) else: content = unicode(content) error_string = unicode(reply.errorString()) logger.debug('_handle_finished error (%s,%s).', error, error_string) if error == QNetworkReply.AuthenticationRequiredError: exception = UnauthorizedError(error_string, content) elif error == QNetworkReply.ProxyAuthenticationRequiredError: # we are going thru a proxy and we did not auth exception = ProxyUnauthorizedError(error_string, content) else: exception = WebClientError(error_string, content) d.errback(exception) def _get_certificate_details(self, cert): """Return an string with the details of the certificate.""" detail_titles = { QSslCertificate.Organization: 'organization', QSslCertificate.CommonName: 'common_name', QSslCertificate.LocalityName: 'locality_name', QSslCertificate.OrganizationalUnitName: 'unit', QSslCertificate.CountryName: 'country_name', QSslCertificate.StateOrProvinceName: 'state_name' } details = {} for info, title in detail_titles.items(): details[title] = str(cert.issuerInfo(info)) return self.format_ssl_details(details) def _get_certificate_host(self, cert): """Return the host of the cert.""" return str(cert.issuerInfo(QSslCertificate.CommonName)) def _handle_ssl_errors(self, reply, errors): """Handle the case in which we got an ssl error.""" msg = StringIO() msg.write('SSL errors found; url: %s\n' % reply.request().url().toString()) for error in errors: msg.write('========Error=============\n%s (%s)\n' % (error.errorString(), error.error())) msg.write('--------Cert Details------\n%s\n' % self._get_certificate_details(error.certificate())) msg.write('==========================\n') logger.error(msg.getvalue()) def force_use_proxy(self, https_settings): """Setup this webclient to use the given proxy settings.""" settings = {"https": https_settings} proxy = build_proxy(settings) self._set_proxy(proxy) WebClient.proxy_instance = proxy def shutdown(self): """Shut down all pending requests (if possible).""" self.nam.deleteLater()
class AboutDialog(QDialog): """Dialog for showing info about TortoiseHg""" def __init__(self, parent=None): super(AboutDialog, self).__init__(parent) self.setWindowIcon(qtlib.geticon('thg_logo')) self.setWindowTitle(_('About')) self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) self.vbox = QVBoxLayout() self.vbox.setSpacing(8) self.logo_lbl = QLabel() self.logo_lbl.setMinimumSize(QSize(92, 50)) self.logo_lbl.setScaledContents(False) self.logo_lbl.setAlignment(Qt.AlignCenter) thglogofile = paths.get_tortoise_icon('thg_logo_92x50.png') self.logo_lbl.setPixmap(QPixmap(thglogofile)) self.vbox.addWidget(self.logo_lbl) self.name_version_libs_lbl = QLabel() self.name_version_libs_lbl.setText(' ') self.name_version_libs_lbl.setAlignment(Qt.AlignCenter) self.name_version_libs_lbl.setTextInteractionFlags( Qt.TextSelectableByMouse) self.vbox.addWidget(self.name_version_libs_lbl) self.getVersionInfo() self.copyright_lbl = QLabel() self.copyright_lbl.setAlignment(Qt.AlignCenter) self.copyright_lbl.setText('\n' + _('Copyright 2008-2013 Steve Borho and others')) self.vbox.addWidget(self.copyright_lbl) self.courtesy_lbl = QLabel() self.courtesy_lbl.setAlignment(Qt.AlignCenter) self.courtesy_lbl.setText( _('Several icons are courtesy of the TortoiseSVN project') + '\n') self.vbox.addWidget(self.courtesy_lbl) self.download_url_lbl = QLabel() self.download_url_lbl.setMouseTracking(True) self.download_url_lbl.setAlignment(Qt.AlignCenter) self.download_url_lbl.setTextInteractionFlags(Qt.LinksAccessibleByMouse) self.download_url_lbl.setOpenExternalLinks(True) self.download_url_lbl.setText('<a href=%s>%s</a>' % ('http://tortoisehg.org', _('You can visit our site here'))) self.vbox.addWidget(self.download_url_lbl) # Let's have some space between the url and the buttons. self.blancline_lbl = QLabel() self.vbox.addWidget(self.blancline_lbl) self.hbox = QHBoxLayout() self.license_btn = QPushButton() self.license_btn.setText(_('&License')) self.license_btn.setAutoDefault(False) self.license_btn.clicked.connect(self.showLicense) self.hspacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) self.close_btn = QPushButton() self.close_btn.setText(_('&Close')) self.close_btn.setDefault(True) self.close_btn.clicked.connect(self.close) self.hbox.addWidget(self.license_btn) self.hbox.addItem(self.hspacer) self.hbox.addWidget(self.close_btn) self.vbox.addLayout(self.hbox) self.setLayout(self.vbox) self.layout().setSizeConstraint(QLayout.SetFixedSize) self._readsettings() # Spawn it later, so that the dialog gets visible quickly. QTimer.singleShot(0, self.getUpdateInfo) self._newverreply = None def getVersionInfo(self): def make_version(tuple): vers = ".".join([str(x) for x in tuple]) return vers thgv = (_('version %s') % version.version()) libv = (_('with Mercurial-%s, Python-%s, PyQt-%s, Qt-%s') % \ (hglib.hgversion, make_version(sys.version_info[0:3]), PYQT_VERSION_STR, QT_VERSION_STR)) par = ('<p style=\" margin-top:0px; margin-bottom:6px;\">' '<span style=\"font-size:%spt; font-weight:600;\">' '%s</span></p>') name = (par % (14, 'TortoiseHg')) thgv = (par % (10, thgv)) nvl = ''.join([name, thgv, libv]) self.name_version_libs_lbl.setText(nvl) @pyqtSlot() def getUpdateInfo(self): verurl = 'http://tortoisehg.bitbucket.org/curversion.txt' # If we use QNetworkAcessManager elsewhere, it should be shared # through the application. self._netmanager = QNetworkAccessManager(self) self._newverreply = self._netmanager.get(QNetworkRequest(QUrl(verurl))) self._newverreply.finished.connect(self.uFinished) @pyqtSlot() def uFinished(self): newver = (0,0,0) try: f = self._newverreply.readAll().data().splitlines() self._newverreply.close() self._newverreply = None newver = tuple([int(p) for p in f[0].split('.')]) upgradeurl = f[1] # generic download URL platform = sys.platform if platform == 'win32': from win32process import IsWow64Process as IsX64 platform = IsX64() and 'x64' or 'x86' # linux2 for Linux, darwin for OSX for line in f[2:]: p, _url = line.split(':', 1) if platform == p: upgradeurl = _url.strip() break except (IndexError, ImportError, ValueError): pass try: thgv = version.version() if '+' in thgv: thgv = thgv[:thgv.index('+')] curver = tuple([int(p) for p in thgv.split('.')]) except ValueError: curver = (0,0,0) if newver > curver: url_lbl = _('A new version of TortoiseHg is ready for download!') urldata = ('<a href=%s>%s</a>' % (upgradeurl, url_lbl)) self.download_url_lbl.setText(urldata) def showLicense(self): from tortoisehg.hgqt import license ld = license.LicenseDialog(self) ld.show() def closeEvent(self, event): if self._newverreply: self._newverreply.abort() self._writesettings() super(AboutDialog, self).closeEvent(event) def _readsettings(self): s = QSettings() self.restoreGeometry(s.value('about/geom').toByteArray()) def _writesettings(self): s = QSettings() s.setValue('about/geom', self.saveGeometry())
class SogisBrowserDock(QDockWidget, Ui_SogisBrowserDock): def __init__(self, parent=None): """Constructor.""" QDockWidget.__init__(self, parent) self.setupUi(self) self.SEARCH_URL = "http://www.catais.org/wsgi/search_metadb_sogis.wsgi?query=" self.toolButtonReset.setIcon(QIcon(':/plugins/sogisbrowser/icons/reset.svg')) today = QDateTime.currentDateTime() self.dateEdit.setDateTime(today) self.dateEdit.setCalendarPopup(True) self.treeWidget = SogisBrowserTreeWidget() self.gridLayout.addWidget(self.treeWidget) # self.dateEdit.setLocale(QLocale(QLocale.German)); # Qt Designer font = QFont() font.setPointSize(10) self.treeWidget.setFont(font) self.networkManager = QNetworkAccessManager(self) self.connect(self.networkManager, SIGNAL("finished(QNetworkReply*)"), self.handleNetworkData) QObject.connect(self.toolButtonReset, SIGNAL("clicked()"), self.resetSuggest) def initGui(self): request = QNetworkRequest(QUrl(self.SEARCH_URL)) self.networkManager.get(request) def handleNetworkData(self, networkReply): url = networkReply.url() if not networkReply.error(): displaytext = [] category = [] meta_id = [] response = networkReply.readAll() try: my_response = unicode(response) json_response = json.loads(my_response, object_pairs_hook=collections.OrderedDict) except Exception: exc_type, exc_value, exc_traceback = sys.exc_info() QMessageBox.critical(None, "SO!GIS Browser", "Failed to load json response" + str(traceback.format_exc(exc_traceback))) return print json_response for result in json_response['results']: displaytext.append(result['displaytext']) category.append(result['category']) meta_id.append(result['meta_id']) self.showSearchResult(displaytext, category, meta_id) def showSearchResult(self, displaytext, category, meta_id): if len(displaytext) == 0: return False pal = self.palette() color = pal.color(QPalette.Disabled, QPalette.WindowText) self.treeWidget.setUpdatesEnabled(False) self.treeWidget.clear() for i in range(len(displaytext)): item = QTreeWidgetItem(self.treeWidget) item.setText(0, displaytext[i]) item.setToolTip(0, displaytext[i]) item.setText(1, category[i]) item.setTextAlignment(1, Qt.AlignRight) item.setTextColor(1, color) item.setData(2, Qt.UserRole, meta_id[i]) self.treeWidget.setUpdatesEnabled(True) def resetSuggest(self): self.treeWidget.clearSelection()
class ImageFetcher(QObject): fetchComplete = pyqtSignal(QByteArray, int) def __init__(self): QObject.__init__(self) self.settings_folder = ComicTaggerSettings.getSettingsFolder() self.db_file = os.path.join(self.settings_folder, "image_url_cache.db") self.cache_folder = os.path.join(self.settings_folder, "image_cache") if not os.path.exists(self.db_file): self.create_image_db() def clearCache(self): os.unlink(self.db_file) if os.path.isdir(self.cache_folder): shutil.rmtree(self.cache_folder) def fetch(self, url, user_data=None, blocking=False): """ If called with blocking=True, this will block until the image is fetched. If called with blocking=False, this will run the fetch in the background, and emit a signal when done """ self.user_data = user_data self.fetched_url = url # first look in the DB image_data = self.get_image_from_cache(url) if blocking: if image_data is None: try: image_data = urllib.urlopen(url).read() except Exception as e: print e raise ImageFetcherException("Network Error!") # save the image to the cache self.add_image_to_cache(self.fetched_url, image_data) return image_data else: # if we found it, just emit the signal asap if image_data is not None: self.fetchComplete.emit(QByteArray(image_data), self.user_data) return # didn't find it. look online self.nam = QNetworkAccessManager() self.nam.finished.connect(self.finishRequest) self.nam.get(QNetworkRequest(QUrl(url))) #we'll get called back when done... def finishRequest(self, reply): # read in the image data image_data = reply.readAll() # save the image to the cache self.add_image_to_cache(self.fetched_url, image_data) self.fetchComplete.emit(QByteArray(image_data), self.user_data) def create_image_db(self): # this will wipe out any existing version open(self.db_file, 'w').close() # wipe any existing image cache folder too if os.path.isdir(self.cache_folder): shutil.rmtree(self.cache_folder) os.makedirs(self.cache_folder) con = lite.connect(self.db_file) # create tables with con: cur = con.cursor() cur.execute("CREATE TABLE Images(" + "url TEXT," + "filename TEXT," + "timestamp TEXT," + "PRIMARY KEY (url) )") def add_image_to_cache(self, url, image_data): con = lite.connect(self.db_file) with con: cur = con.cursor() timestamp = datetime.datetime.now() tmp_fd, filename = tempfile.mkstemp(dir=self.cache_folder, prefix="img") f = os.fdopen(tmp_fd, 'w+b') f.write(image_data) f.close() cur.execute("INSERT or REPLACE INTO Images VALUES( ?, ?, ? )", (url, filename, timestamp)) def get_image_from_cache(self, url): con = lite.connect(self.db_file) with con: cur = con.cursor() cur.execute("SELECT filename FROM Images WHERE url=?", [url]) row = cur.fetchone() if row is None: return None else: filename = row[0] image_data = None try: with open(filename, 'rb') as f: image_data = f.read() f.close() except IOError as e: pass return image_data
class downloadManager(QtCore.QObject): ''' This class allows downloading stuff in the background''' def __init__(self, parent = None): self.client = parent self.nam = QNetworkAccessManager() self.nam.finished.connect(self.finishedDownload) self.modRequests = {} self.mapRequests = {} self.mapRequestsItem = [] def finishedDownload(self,reply): ''' finishing downloads ''' urlstring = reply.url().toString() reqlist = [] if urlstring in self.mapRequests: reqlist = self.mapRequests[urlstring] if urlstring in self.modRequests: reqlist = self.modRequests[urlstring] if reqlist: #save the map from cache name = os.path.basename(reply.url().toString()) pathimg = os.path.join(util.CACHE_DIR, name) img = QtCore.QFile(pathimg) img.open(QtCore.QIODevice.WriteOnly) img.write(reply.readAll()) img.close() if os.path.exists(pathimg): #Create alpha-mapped preview image try: pass # the server already sends 100x100 pic # img = QtGui.QImage(pathimg).scaled(100,100) # img.save(pathimg) except: pathimg = "games/unknown_map.png" logger.info("Failed to resize " + name) else : pathimg = "games/unknown_map.png" logger.debug("Web Preview failed for: " + name) logger.debug("Web Preview used for: " + name) for requester in reqlist: if requester: if requester in self.mapRequestsItem: requester.setIcon(0, util.icon(pathimg, False)) self.mapRequestsItem.remove(requester) else: requester.setIcon(util.icon(pathimg, False)) if urlstring in self.mapRequests: del self.mapRequests[urlstring] if urlstring in self.modRequests: del self.modRequests[urlstring] def downloadMap(self, name, requester, item=False): ''' Downloads a preview image from the web for the given map name ''' #This is done so generated previews always have a lower case name. This doesn't solve the underlying problem (case folding Windows vs. Unix vs. FAF) name = name.lower() if len(name) == 0: return url = QtCore.QUrl(VAULT_PREVIEW_ROOT + urllib2.quote(name) + ".png") if not url.toString() in self.mapRequests: logger.debug("Searching map preview for: " + name) self.mapRequests[url.toString()] = [] request = QNetworkRequest(url) self.nam.get(request) self.mapRequests[url.toString()].append(requester) else : self.mapRequests[url.toString()].append(requester) if item: self.mapRequestsItem.append(requester) def downloadModPreview(self, strurl, requester): url = QtCore.QUrl(strurl) if not url.toString() in self.modRequests: logger.debug("Searching mod preview for: " + os.path.basename(strurl).rsplit('.',1)[0]) self.modRequests[url.toString()] = [] request = QNetworkRequest(url) self.nam.get(request) self.modRequests[url.toString()].append(requester)
class MapTileHTTPLoader(QObject): tileLoaded = pyqtSignal(int, int, int, QByteArray) def __init__(self, cacheSize=1024*1024*100, userAgent='(PyQt) TileMap 1.2', parent=None): QObject.__init__(self, parent=parent) self._manager = None self._cache = None self._cacheSize = cacheSize self._userAgent = userAgent self._tileInDownload = dict() @pyqtSlot(int, int, int, str) def loadTile(self, x, y, zoom, url): if self._manager is None: self._manager = QNetworkAccessManager(parent=self) self._manager.finished.connect(self.handleNetworkData) self._cache = MapTileHTTPCache(maxSize=self._cacheSize, parent=self) key = (x, y, zoom) url = QUrl(url) if url in self._cache: # print('from cache') data = self._cache[url] self.tileLoaded.emit(x, y, zoom, data) elif key in self._tileInDownload: # Image is already in download... return return else: request = QNetworkRequest(url=url) request.setRawHeader('User-Agent', self._userAgent) request.setAttribute(QNetworkRequest.User, key) self._tileInDownload[key] = self._manager.get(request) # print('In download:', len(self._tileInDownload)) @pyqtSlot(QNetworkReply) def handleNetworkData(self, reply): tp = reply.request().attribute(QNetworkRequest.User) # .toPyObject() if tp in self._tileInDownload: del self._tileInDownload[tp] if not reply.error(): data = reply.readAll() self._cache[reply.request().url()] = data self.tileLoaded.emit(tp[0], tp[1], tp[2], data) reply.close() reply.deleteLater() @pyqtSlot() def abortRequest(self, x, y, zoom): p = (x, y, zoom) reply = self._tileInDownload[p] del self._tileInDownload[p] reply.close() reply.deleteLater() @pyqtSlot() def abortAllRequests(self): for x, y, zoom in list(self._tileInDownload.keys()): self.abortRequest(x, y, zoom)
class GasWmtsLayer(QObject): def __init__(self, iface, data, fallback = False): QObject.__init__(self) self.iface = iface self.canvas = self.iface.mapCanvas() self.fallback = fallback self.settings = QSettings("CatAIS","GeoAdminSearch") self.wmtsCapabilitities = self.settings.value("services/wmtscapabilities", "http://api3.geo.admin.ch/rest/services/api/1.0.0/WMTSCapabilities.xml") self.settings = QSettings("CatAIS","GeoAdminSearch") searchLanguage = self.settings.value("options/language", "de") self.userName = self.settings.value("options/username", "") self.password = self.settings.value("options/password", "") self.layerName = data['layer'] url = self.wmtsCapabilitities # It does not work: # a) when networkAccess is not 'self' # b) without lambda self.networkAccess = QNetworkAccessManager() self.connect(self.networkAccess, SIGNAL("finished(QNetworkReply*)"), lambda event, data=data: self.receiveWmtsCapabilities(event, data)) self.networkAccess.get(QNetworkRequest(QUrl(url))) def receiveWmtsCapabilities(self, networkReply, data): if not networkReply.error(): response = networkReply.readAll() xml = QXmlStreamReader(response) while not xml.atEnd(): token = xml.readNext() if token == QXmlStreamReader.StartDocument: continue if token == QXmlStreamReader.StartElement: if xml.name() == "Layer": identifier = None format = None time = None tileMatrixSet = None xml.readNext() while not (xml.tokenType() == QXmlStreamReader.EndElement and xml.name() == "Layer"): if xml.tokenType() == QXmlStreamReader.StartElement: if xml.name() == "Identifier" and identifier == None: my_identifier = xml.readElementText().strip() if my_identifier == self.layerName: identifier = my_identifier if xml.name() == "Format" and format == None: my_format = xml.readElementText().strip() if xml.name() == "Dimension" and time == None: while not (xml.tokenType() == QXmlStreamReader.EndElement and xml.name() == "Dimension"): if xml.tokenType() == QXmlStreamReader.StartElement: if xml.name() == "Default": my_time = xml.readElementText().strip() xml.readNext() if xml.name() == "TileMatrixSetLink" and tileMatrixSet == None: while not (xml.tokenType() == QXmlStreamReader.EndElement and xml.name() == "TileMatrixSetLink"): if xml.tokenType() == QXmlStreamReader.StartElement: if xml.name() == "TileMatrixSet": my_tileMatrixSet = xml.readElementText().strip() xml.readNext() xml.readNext() if identifier <> None: format = my_format time = my_time tileMatrixSet = my_tileMatrixSet break print "end of WMTSCapabilities.xml" print identifier if not identifier or not format or not time or not tileMatrixSet: if not self.fallback: self.iface.messageBar().pushMessage("Warning", _translate("GeoAdminSearch", "WMTS layer not found. Will try to add it as WMS layer.", None), level=QgsMessageBar.WARNING, duration=5) self.emit(SIGNAL("wmtsLayerNotFound(QVariant, QString)"), data, "WMS") return else: self.iface.messageBar().pushMessage("Error", _translate("GeoAdminSearch", "Not able to add WMS or WMTS layer.", None), level=QgsMessageBar.CRITICAL, duration=5) return QApplication.setOverrideCursor(Qt.WaitCursor) try: headerFields = self.settings.value("options/headerfields") headerValues = self.settings.value("options/headervalues") referer ="" if headerFields and headerValues: for i in range(len(headerFields)): if headerFields[i] == "Referer": referer = headerValues[i] layerName = data['label'].replace('<b>', '').replace('</b>', '') if referer == "": uri = "crs=EPSG:21781&dpiMode=7&featureCount=10&format="+format+"&layers="+identifier+"&styles=&tileDimensions=Time%3D"+time+"&tileMatrixSet="+tileMatrixSet+"&url=" + self.wmtsCapabilitities else: uri = "crs=EPSG:21781&dpiMode=7&featureCount=10&format="+format+"&layers="+identifier+"&referer="+referer+"&styles=&tileDimensions=Time%3D"+time+"&tileMatrixSet="+tileMatrixSet+"&url=" + self.wmtsCapabilitities wmtsLayer = QgsRasterLayer (uri, layerName, "wms", False) self.emit(SIGNAL("layerCreated(QgsMapLayer)"), wmtsLayer) except: exc_type, exc_value, exc_traceback = sys.exc_info() print str(traceback.format_exc(exc_traceback)) QApplication.restoreOverrideCursor() QApplication.restoreOverrideCursor()
class StakeholderProtocol(QObject): # SIGNAL that is emitted after stakeholders have been read readSignal = pyqtSignal(bool, int, QString) # SIGNAL that is emitted after a new stakeholder has been added created = pyqtSignal(bool, int, QString) # SIGNAL that is emitted after an existing activity has been updated updated = pyqtSignal(bool, int, QString) # SIGNAL that is emitted after an activity has been deleted deleted = pyqtSignal(bool, int, QString) # SIGNAL that is emitted after activities have been counted counted = pyqtSignal(bool, int, QString) def __init__(self, host, user, password): QObject.__init__(self) # Set the host self.host = host # Create a base64 encoded credential string from user name and password. # This is required for the HTTP basic access authentication, compare # also http://en.wikipedia.org/wiki/Basic_access_authentication self.userlogin = "******" % (user, password) self.login = "******" + QByteArray(self.userlogin).toBase64() # Create a new QNetworkAccessManager and connect it to the # authenticationRequired signal self.manager = QNetworkAccessManager(self) # self.connect(self.manager, SIGNAL("authenticationRequired( QNetworkReply*, QAuthenticator* )"), self.slotAuthenticationRequired) def read(self, **kwargs): self.connect(self.manager, SIGNAL("finished( QNetworkReply* )"), self.readRequestFinished) params = [] try: queryable = kwargs["queryable"] params.append({"queryable": queryable}) queryableList = QString(queryable).split(",") except KeyError: pass for key, value in kwargs.items(): if QString(key).split("__")[0] in queryableList: params.append({key: value}) url = "%s/stakeholders?" % self.host for p in params: url = "%s%s=%s&" % (url, p.keys()[0], p.values()[0]) qUrl = QUrl(url) # Create a new request request = QNetworkRequest(qUrl) request.setRawHeader("Authorization", self.login) request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json") self.manager.get(request) return url def readRequestFinished(self, reply): # Get the HTTP status code from the reply self.disconnect(self.manager, SIGNAL("finished( QNetworkReply* )"), self.readRequestFinished) httpStatusCode = int(reply.attribute(QNetworkRequest.HttpStatusCodeAttribute).toString()) # Check the status code see also http://en.wikipedia.org/wiki/HTTP_status_code # In case of a successful upload we get a 201 status code back and the # "stylePosted" signal is emitted with the first parameter set to True. # If the query didn't succeed, the status code is 4xx indicating that # the host was not found, the authentication failed or a forbidden # action in case a style with the same name already exists. It's up to # the receiver to handle these status codes. self.readSignal.emit(httpStatusCode in (200, 201), httpStatusCode, QString(reply.readAll())) def add(self, stakeholder): self.connect(self.manager, SIGNAL("finished( QNetworkReply* )"), self.readRequestFinished) url = "%s/stakeholders" % self.host qurl = QUrl(url) # Create a new request request = QNetworkRequest(qurl) request.setRawHeader("Authorization", self.login) request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json") wrapperObj = {} if len(stakeholder) > 0: wrapperObj["stakeholders"] = [s.createDiff(None) for s in stakeholder] else: wrapperObj["stakeholders"] = [stakeholder.createDiff(None)] rawBody = json.dumps(wrapperObj, sort_keys=True, indent=4 * " ") self.manager.post(request, rawBody) return url, rawBody def addRequestFinished(self, reply): self.disconnect(self.manager, SIGNAL("finished( QNetworkReply* )"), self.readRequestFinished) httpStatusCode = int(reply.attribute(QNetworkRequest.HttpStatusCodeAttribute).toString()) self.created.emit(httpStatusCode in (200, 201), httpStatusCode, QString(reply.readAll()))
class MikiEdit(QTextEdit): def __init__(self, parent=None): super(MikiEdit, self).__init__(parent) self.parent = parent self.settings = parent.settings self.setFontPointSize(12) self.setVisible(False) self.ix = open_dir(self.settings.indexdir) # Spell checker support try: import enchant enchant.Dict() self.speller = enchant.Dict() except ImportError: print("Spell checking unavailable. Need to install pyenchant.") self.speller = None except enchant.errors.DictNotFoundError: print( "Spell checking unavailable. Need to install dictionary (e.g. aspell-en)." ) self.speller = None self.imageFilter = "" self.documentFilter = "" for ext in self.settings.attachmentImage: self.imageFilter += " *" + ext for ext in self.settings.attachmentDocument: self.documentFilter += " *" + ext self.imageFilter = "Image (" + self.imageFilter.strip() + ")" self.documentFilter = "Document (" + self.documentFilter.strip() + ")" self.downloadAs = "" self.networkManager = QNetworkAccessManager() self.networkManager.finished.connect(self.downloadFinished) def updateIndex(self): ''' Update whoosh index, which cost much computing resource ''' page = self.parent.notesTree.currentPage() content = self.toPlainText() try: #writer = self.ix.writer() writer = AsyncWriter(self.ix) if METADATA_CHECKER.match( content) and 'meta' in self.settings.extensions: no_metadata_content = METADATA_CHECKER.sub("", content, count=1).lstrip() self.settings.md.reset().convert(content) writer.update_document( path=page, title=parseTitle(content, page), content=no_metadata_content, tags=','.join(self.settings.md.Meta.get('tags', [])).strip()) writer.commit() else: writer.update_document(path=page, title=parseTitle(content, page), content=content, tags='') writer.commit() except: print("Whoosh commit failed.") def downloadFinished(self, reply): if reply.error(): print("Failed to download") else: attFile = QFile(self.downloadAs) attFile.open(QIODevice.WriteOnly) attFile.write(reply.readAll()) attFile.close() print("Succeeded") reply.deleteLater() def mimeFromText(self, text): mime = QMimeData() mime.setText(text) return mime def createMimeDataFromSelection(self): """ Reimplement this to prevent copied text taken as hasHtml() """ plaintext = self.textCursor().selectedText() # From QTextCursor doc: # if the selection obtained from an editor spans a line break, # the text will contain a Unicode U+2029 paragraph separator character # instead of a newline \n character text = plaintext.replace('\u2029', '\n') return self.mimeFromText(text) def insertFromMimeData(self, source): """ Intended behavior If copy/drag something that hasUrls, then check the extension name: if image then apply image pattern ![Alt text](/path/to/img.jpg) else apply link pattern [text](http://example.net) If copy/drag something that hasImage, then ask for file name If copy/drag something that hasHtml, then html2text Else use the default insertFromMimeData implementation """ item = self.parent.notesTree.currentItem() attDir = self.parent.notesTree.itemToAttachmentDir(item) if not QDir(attDir).exists(): QDir().mkpath(attDir) if source.hasUrls(): for qurl in source.urls(): url = qurl.toString() filename, extension = os.path.splitext(url) filename = os.path.basename(filename) newFilePath = os.path.join(attDir, filename + extension).replace( os.sep, '/') relativeFilePath = newFilePath.replace( self.settings.notebookPath, "..") attachments = self.settings.attachmentImage + self.settings.attachmentDocument if QUrl(qurl).isLocalFile(): if extension.lower() in attachments: nurl = url.replace("file://", "") QFile.copy(nurl, newFilePath) self.parent.updateAttachmentView() if extension.lower() in self.settings.attachmentImage: text = "![%s](%s)" % (filename, relativeFilePath) elif extension.lower( ) in self.settings.attachmentDocument: text = "[%s%s](%s)\n" % (filename, extension, relativeFilePath) else: text = "[%s%s](%s)\n" % (filename, extension, url) else: if extension.lower() in attachments: self.downloadAs = newFilePath self.networkManager.get(QNetworkRequest(qurl)) if extension.lower() in self.settings.attachmentImage: text = "![%s](%s)" % (filename, relativeFilePath) elif extension.lower( ) in self.settings.attachmentDocument: text = "[%s%s](%s)\n" % (filename, extension, relativeFilePath) else: text = "[%s%s](%s)\n" % (filename, extension, url) super(MikiEdit, self).insertFromMimeData(self.mimeFromText(text)) elif source.hasImage(): img = source.imageData() attDir = self.parent.notesTree.itemToAttachmentDir(item) dialog = LineEditDialog(attDir, self) if dialog.exec_(): fileName = dialog.editor.text() if not QFileInfo(fileName).suffix(): fileName += '.jpg' filePath = os.path.join(attDir, fileName).replace(os.sep, '/') img.save(filePath) relativeFilePath = filePath.replace(self.settings.notebookPath, "..") text = "![%s](%s)" % (fileName, relativeFilePath) super(MikiEdit, self).insertFromMimeData(self.mimeFromText(text)) elif source.hasHtml(): html = source.html() if HAS_HTML2TEXT: backToMarkdown = html2text.HTML2Text() markdown = backToMarkdown.handle(html) super(MikiEdit, self).insertFromMimeData(self.mimeFromText(markdown)) else: super(MikiEdit, self).insertFromMimeData(self.mimeFromText(html)) else: super(MikiEdit, self).insertFromMimeData(source) def insertAttachment(self, filePath, fileType): item = self.parent.notesTree.currentItem() attDir = self.parent.notesTree.itemToAttachmentDir(item) filename, extension = os.path.splitext(filePath) filename = os.path.basename(filename) newFilePath = os.path.join(attDir, filename + extension).replace(os.sep, '/') relativeFilePath = newFilePath.replace(self.settings.notebookPath, "..") if not os.path.exists(attDir): os.makedirs(attDir) QFile.copy(filePath, newFilePath) self.parent.updateAttachmentView() if fileType == self.imageFilter: text = "![%s](%s)" % (filename, relativeFilePath) else: text = "[%s%s](%s)\n" % (filename, extension, relativeFilePath) self.insertPlainText(text) def insertAttachmentWrapper(self): (filePath, fileType) = QFileDialog.getOpenFileNameAndFilter( self, self.tr('Insert attachment'), '', self.imageFilter + ";;" + self.documentFilter) if filePath == "": return self.insertAttachment(filePath, fileType) def contextMenuEvent(self, event): def correctWord(cursor, word): # From QTextCursor doc: # if there is a selection, the selection is deleted and replaced return lambda: cursor.insertText(word) popup_menu = self.createStandardContextMenu() # Spellcheck the word under mouse cursor, not self.textCursor cursor = self.cursorForPosition(event.pos()) cursor.select(QTextCursor.WordUnderCursor) text = cursor.selectedText() if self.speller and text: if not self.speller.check(text): lastAction = popup_menu.actions()[0] for word in self.speller.suggest(text)[:10]: action = QAction(word, popup_menu) action.triggered.connect(correctWord(cursor, word)) action.setFont(QFont("sans", weight=QFont.Bold)) popup_menu.insertAction(lastAction, action) popup_menu.insertSeparator(lastAction) popup_menu.exec_(event.globalPos()) def keyPressEvent(self, event): """ for Qt.Key_Tab, expand as 4 spaces for other keys, use default implementation """ if event.key() == Qt.Key_Tab: self.insertPlainText(' ') else: QTextEdit.keyPressEvent(self, event) ''' def closeEvent(self, event): self.ix.close() print('closed idx') event.accept() ''' def save(self, item): pageName = self.parent.notesTree.itemToPage(item) filePath = self.parent.notesTree.itemToFile(item) htmlFile = self.parent.notesTree.itemToHtmlFile(item) fh = QFile(filePath) try: if not fh.open(QIODevice.WriteOnly): raise IOError(fh.errorString()) except IOError as e: QMessageBox.warning(self, 'Save Error', 'Failed to save %s: %s' % (pageName, e)) raise finally: if fh is not None: savestream = QTextStream(fh) savestream << self.toPlainText() fh.close() self.document().setModified(False) # Fork a process to update index, which benefit responsiveness. Thread(target=self.updateIndex).start() def toHtml(self): '''markdown.Markdown.convert v.s. markdown.markdown ~~Previously `convert` was used, but it doens't work with fenced_code~~ fixed that by calling markdown.Markdown.reset before each conversion ''' htmltext = self.toPlainText() if 'mdx_asciimathml' in self.settings.extensions: stuff = JSCRIPT_TPL.format(self.settings.mathjax) else: stuff = '' return self.settings.md.reset().convert(htmltext) + stuff # md = markdown.Markdown(extensions) # return md.convert(htmltext) def saveAsHtml(self, htmlFile=None): """ Save as Complete (with css and images) or HTML Only To be merged with saveNoteAs """ if not htmlFile: (htmlFile, htmlType) = QFileDialog.getSaveFileNameAndFilter( self, self.tr("Export to HTML"), "", "Complete;;HTML Only") if htmlFile == '': return if not QFileInfo(htmlFile).suffix(): htmlFile += '.html' if htmlType == "Complete": self.saveCompleteHtml(htmlFile) else: self.saveHtmlOnly(htmlFile) def saveCompleteHtml(self, htmlFile): html = QFile(htmlFile) html.open(QIODevice.WriteOnly) savestream = QTextStream(html) css = QFile(self.settings.cssfile) css.open(QIODevice.ReadOnly) # Use a html lib may be a better idea? savestream << "<html><head><meta charset='utf-8'></head>" # Css is inlined. savestream << "<style>" savestream << QTextStream(css).readAll() savestream << "</style>" # Note content savestream << self.toHtml() savestream << "</html>" html.close() def saveHtmlOnly(self, htmlFile): fileDir = os.path.dirname(htmlFile) QDir().mkpath(fileDir) html = QFile(htmlFile) html.open(QIODevice.WriteOnly) savestream = QTextStream(html) savestream << """ <html><head> <meta charset="utf-8"> <link rel="stylesheet" href="/css/notebook.css" type="text/css" /> </head> """ # Note content savestream << self.toHtml() savestream << "</html>" html.close()
class ActivityProtocol(QObject): # SIGNAL that is emitted after activities have been read readSignal = pyqtSignal(bool, int, QString) # SIGNAL that is emitted after a new activity has been added created = pyqtSignal(bool, int, QString) # SIGNAL that is emitted after an existing activity has been updated updated = pyqtSignal(bool, int, QString) # SIGNAL that is emitted after an activity has been deleted deleted = pyqtSignal(bool, int, QString) # SIGNAL that is emitted after activities have been counted counted = pyqtSignal(bool, int, QString) def __init__(self, host, user, password): QObject.__init__(self) # Set the host self.host = host # Create a base64 encoded credential string from user name and password. # This is required for the HTTP basic access authentication, compare # also http://en.wikipedia.org/wiki/Basic_access_authentication self.userlogin = "******" % (user, password) self.login = "******" + QByteArray(self.userlogin).toBase64() # Create a new QNetworkAccessManager and connect it to the # authenticationRequired signal self.manager = QNetworkAccessManager(self) # self.connect(self.manager, SIGNAL("authenticationRequired( QNetworkReply*, QAuthenticator* )"), self.slotAuthenticationRequired) def update(self, rawBody): """ Update an activity using a POST request """ self.connect(self.manager, SIGNAL("finished( QNetworkReply* )"), self.updateRequestFinished) url = "%s/activities" % self.host qurl = QUrl(url) self.request = QNetworkRequest(qurl) self.request.setRawHeader("Authorization", self.login) self.request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json") self.manager.post(self.request, QString(json.dumps(rawBody)).toUtf8()) return url def updateRequestFinished(self, reply): self.disconnect(self.manager, SIGNAL("finished( QNetworkReply* )"), self.updateRequestFinished) # Get the HTTP status code from the reply self.httpStatusCode = int(reply.attribute(QNetworkRequest.HttpStatusCodeAttribute).toString()) # httpReasonPhrase = reply.attribute(QNetworkRequest.HttpReasonPhraseAttribute).toString() data = reply.readAll() self.updated.emit(self.httpStatusCode in (200, 201), self.httpStatusCode, QString(data)) def read(self, extent): self.connect(self.manager, SIGNAL("finished( QNetworkReply* )"), self.readRequestFinished) # Limit the longitude and latitutde maximum boundaries xmin = extent.xMinimum() if extent.xMinimum() >= -180 else -180 ymin = extent.yMinimum() if extent.yMinimum() >= -90 else -90 xmax = extent.xMaximum() if extent.xMaximum() <= 180 else 180 ymax = extent.yMaximum() if extent.yMaximum() <= 90 else 90 url = "%s/activities/json?bbox=%d,%d,%d,%d" % (self.host, xmin, ymin, xmax, ymax) qUrl = QUrl(url) self.request = QNetworkRequest(qUrl) self.request.setRawHeader("Authorization", self.login) self.request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json") self.manager.get(self.request) return url def readRequestFinished(self, reply): # Get the HTTP status code from the reply self.httpStatusCode = int(reply.attribute(QNetworkRequest.HttpStatusCodeAttribute).toString()) # data = str("a") data = reply.readAll() # Check the status code see also http://en.wikipedia.org/wiki/HTTP_status_code # In case of a successful upload we get a 201 status code back and the # "stylePosted" signal is emitted with the first parameter set to True. # If the query didn't succeed, the status code is 4xx indicating that # the host was not found, the authentication failed or a forbidden # action in case a style with the same name already exists. It's up to # the receiver to handle these status codes. self.readSignal.emit(self.httpStatusCode in (200, 201), self.httpStatusCode, QString(data)) def readById(self, id): """ """ self.connect(self.manager, SIGNAL("finished( QNetworkReply* )"), self.readByIdRequestFinished) # Get the latest version of the activity with this id url = "%s/activities/json/%s?geometry=full" % (self.host, id) qUrl = QUrl(url) self.request = QNetworkRequest(qUrl) # self.request.setRawHeader("Authorization", self.login) self.manager.get(self.request) return url def readByIdRequestFinished(self, reply): # Get the HTTP status code from the reply self.httpStatusCode = int(reply.attribute(QNetworkRequest.HttpStatusCodeAttribute).toString()) data = reply.readAll() self.readSignal.emit(self.httpStatusCode in (200, 201), self.httpStatusCode, QString(data))
class ReplaysWidget(BaseClass, FormClass): SOCKET = 11002 HOST = "lobby.faforever.com" def __init__(self, client): super(BaseClass, self).__init__() self.setupUi(self) #self.replayVault.setVisible(False) self.client = client client.replaysTab.layout().addWidget(self) client.gameInfo.connect(self.processGameInfo) client.replayVault.connect(self.replayVault) self.onlineReplays = {} self.onlineTree.setItemDelegate(ReplayItemDelegate(self)) self.replayDownload = QNetworkAccessManager() self.replayDownload.finished.connect(self.finishRequest) # sending request to replay vault self.searchButton.pressed.connect(self.searchVault) self.playerName.returnPressed.connect(self.searchVault) self.mapName.returnPressed.connect(self.searchVault) self.myTree.itemDoubleClicked.connect(self.myTreeDoubleClicked) self.myTree.itemPressed.connect(self.myTreePressed) self.myTree.header().setResizeMode(0, QtGui.QHeaderView.ResizeToContents) self.myTree.header().setResizeMode(1, QtGui.QHeaderView.ResizeToContents) self.myTree.header().setResizeMode(2, QtGui.QHeaderView.Stretch) self.myTree.header().setResizeMode(3, QtGui.QHeaderView.ResizeToContents) self.liveTree.itemDoubleClicked.connect(self.liveTreeDoubleClicked) self.liveTree.itemPressed.connect(self.liveTreePressed) self.liveTree.header().setResizeMode( 0, QtGui.QHeaderView.ResizeToContents) self.liveTree.header().setResizeMode(1, QtGui.QHeaderView.Stretch) self.liveTree.header().setResizeMode( 2, QtGui.QHeaderView.ResizeToContents) self.games = {} self.onlineTree.itemDoubleClicked.connect(self.onlineTreeDoubleClicked) self.onlineTree.itemPressed.connect(self.onlineTreeClicked) # replay vault connection to server self.searching = False self.blockSize = 0 self.replayVaultSocket = QtNetwork.QTcpSocket() self.replayVaultSocket.error.connect(self.handleServerError) self.replayVaultSocket.readyRead.connect(self.readDataFromServer) self.replayVaultSocket.disconnected.connect(self.disconnected) self.replayVaultSocket.error.connect(self.errored) logger.info("Replays Widget instantiated.") def searchVault(self): ''' search for some replays ''' self.searching = True self.connectToModVault() self.send( dict(command="search", rating=self.minRating.value(), map=self.mapName.text(), player=self.playerName.text(), mod=self.modList.currentText())) self.onlineTree.clear() def reloadView(self): if self.searching != True: self.connectToModVault() self.send(dict(command="list")) def finishRequest(self, reply): faf_replay = QtCore.QFile( os.path.join(util.CACHE_DIR, "temp.fafreplay")) faf_replay.open(QtCore.QIODevice.WriteOnly | QtCore.QIODevice.Truncate) faf_replay.write(reply.readAll()) faf_replay.flush() faf_replay.close() fa.exe.replay(os.path.join(util.CACHE_DIR, "temp.fafreplay")) def onlineTreeClicked(self, item): if QtGui.QApplication.mouseButtons() == QtCore.Qt.RightButton: item.pressed(item) else: if hasattr(item, "moreInfo"): if item.moreInfo == False: self.connectToModVault() self.send(dict(command="info_replay", uid=item.uid)) else: self.replayInfos.clear() self.replayInfos.setHtml(item.replayInfo) def onlineTreeDoubleClicked(self, item): if hasattr(item, "url"): self.replayDownload.get(QNetworkRequest(QtCore.QUrl(item.url))) def replayVault(self, message): action = message["action"] if action == "list_recents": self.onlineReplays = {} replays = message["replays"] for replay in replays: uid = replay["id"] if uid not in self.onlineReplays: self.onlineReplays[uid] = ReplayItem(uid, self) self.onlineReplays[uid].update(replay, self.client) else: self.onlineReplays[uid].update(replay, self.client) self.updateOnlineTree() elif action == "info_replay": uid = message["uid"] if uid in self.onlineReplays: self.onlineReplays[uid].infoPlayers(message["players"]) elif action == "search_result": self.searching = False self.onlineReplays = {} replays = message["replays"] for replay in replays: uid = replay["id"] if uid not in self.onlineReplays: self.onlineReplays[uid] = ReplayItem(uid, self) self.onlineReplays[uid].update(replay, self.client) else: self.onlineReplays[uid].update(replay, self.client) self.updateOnlineTree() def focusEvent(self, event): self.updatemyTree() self.reloadView() return BaseClass.focusEvent(self, event) def showEvent(self, event): self.updatemyTree() self.reloadView() return BaseClass.showEvent(self, event) def updateOnlineTree(self): self.replayInfos.clear() self.onlineTree.clear() buckets = {} for uid in self.onlineReplays: bucket = buckets.setdefault(self.onlineReplays[uid].startDate, []) bucket.append(self.onlineReplays[uid]) for bucket in buckets.keys(): bucket_item = QtGui.QTreeWidgetItem() self.onlineTree.addTopLevelItem(bucket_item) bucket_item.setIcon(0, util.icon("replays/bucket.png")) bucket_item.setText(0, "<font color='white'>" + bucket + "</font>") bucket_item.setText( 1, "<font color='white'>" + str(len(buckets[bucket])) + " replays</font>") for replay in buckets[bucket]: bucket_item.addChild(replay) replay.setFirstColumnSpanned(True) replay.setIcon(0, replay.icon) bucket_item.setExpanded(True) def updatemyTree(self): self.myTree.clear() # We put the replays into buckets by day first, then we add them to the treewidget. buckets = {} # Iterate for infile in os.listdir(util.REPLAY_DIR): if infile.endswith(".scfareplay"): bucket = buckets.setdefault("legacy", []) item = QtGui.QTreeWidgetItem() item.setText(1, infile) item.filename = os.path.join(util.REPLAY_DIR, infile) item.setIcon(0, util.icon("replays/replay.png")) item.setTextColor( 0, QtGui.QColor(client.instance.getColor("default"))) bucket.append(item) elif infile.endswith(".fafreplay"): item = QtGui.QTreeWidgetItem() try: item.filename = os.path.join(util.REPLAY_DIR, infile) item.info = json.loads( open(item.filename, "rt").readline()) # Parse replayinfo into data if item.info.get('complete', False): game_date = time.strftime( "%Y-%m-%d", time.localtime(item.info['game_time'])) game_hour = time.strftime( "%H:%M", time.localtime(item.info['game_time'])) bucket = buckets.setdefault(game_date, []) icon = fa.maps.preview(item.info['mapname']) if icon: item.setIcon(0, icon) else: self.client.downloader.downloadMap( item.info['mapname'], item, True) item.setIcon(0, util.icon("games/unknown_map.png")) item.setToolTip( 0, fa.maps.getDisplayName(item.info['mapname'])) item.setText(0, game_hour) item.setTextColor( 0, QtGui.QColor(client.instance.getColor("default"))) item.setText(1, item.info['title']) item.setToolTip(1, infile) # Hacky way to quickly assemble a list of all the players, but including the observers playerlist = [] for _, players in item.info['teams'].items(): playerlist.extend(players) item.setText(2, ", ".join(playerlist)) item.setToolTip(2, ", ".join(playerlist)) # Add additional info item.setText(3, item.info['featured_mod']) item.setTextAlignment(3, QtCore.Qt.AlignCenter) item.setTextColor( 1, QtGui.QColor( client.instance.getUserColor( item.info.get('recorder', "")))) else: bucket = buckets.setdefault("incomplete", []) item.setIcon(0, util.icon("replays/replay.png")) item.setText(1, infile) item.setText( 2, "(replay doesn't have complete metadata)") item.setTextColor(1, QtGui.QColor( "yellow")) #FIXME: Needs to come from theme except: bucket = buckets.setdefault("broken", []) item.setIcon(0, util.icon("replays/broken.png")) item.setText(1, infile) item.setTextColor( 1, QtGui.QColor("red")) #FIXME: Needs to come from theme item.setText(2, "(replay parse error)") item.setTextColor( 2, QtGui.QColor("gray")) #FIXME: Needs to come from theme logger.error("Replay parse error for " + infile) bucket.append(item) # Now, create a top level treewidgetitem for every bucket, and put the bucket's contents into them for bucket in buckets.keys(): bucket_item = QtGui.QTreeWidgetItem() if bucket == "broken": bucket_item.setTextColor( 0, QtGui.QColor("red")) #FIXME: Needs to come from theme bucket_item.setText(1, "(not watchable)") bucket_item.setTextColor( 1, QtGui.QColor(client.instance.getColor("default"))) elif bucket == "incomplete": bucket_item.setTextColor( 0, QtGui.QColor("yellow")) #FIXME: Needs to come from theme bucket_item.setText(1, "(watchable)") bucket_item.setTextColor( 1, QtGui.QColor(client.instance.getColor("default"))) elif bucket == "legacy": bucket_item.setTextColor( 0, QtGui.QColor(client.instance.getColor("default"))) bucket_item.setTextColor( 1, QtGui.QColor(client.instance.getColor("default"))) bucket_item.setText(1, "(old replay system)") else: bucket_item.setTextColor( 0, QtGui.QColor(client.instance.getColor("player"))) bucket_item.setIcon(0, util.icon("replays/bucket.png")) bucket_item.setText(0, bucket) bucket_item.setText(3, str(len(buckets[bucket])) + " replays") bucket_item.setTextColor( 3, QtGui.QColor(client.instance.getColor("default"))) self.myTree.addTopLevelItem(bucket_item) #self.myTree.setFirstItemColumnSpanned(bucket_item, True) for replay in buckets[bucket]: bucket_item.addChild(replay) def displayReplay(self): for uid in self.games: item = self.games[uid] if time.time() - item.info[ 'game_time'] > LIVEREPLAY_DELAY_TIME and item.isHidden(): item.setHidden(False) @QtCore.pyqtSlot(dict) def processGameInfo(self, info): if info['state'] == "playing": if info['uid'] in self.games: # Updating an existing item item = self.games[info['uid']] item.takeChildren( ) #Clear the children of this item before we're updating it else: # Creating a fresh item item = QtGui.QTreeWidgetItem() self.games[info['uid']] = item self.liveTree.insertTopLevelItem(0, item) if time.time() - info["game_time"] < LIVEREPLAY_DELAY_TIME: item.setHidden(True) QtCore.QTimer.singleShot( LIVEREPLAY_DELAY_QTIMER, self.displayReplay ) #The delay is there because we have a delay in the livereplay server # For debugging purposes, format our tooltip for the top level items # so it contains a human-readable representation of the info dictionary item.info = info tip = "" for key in info.keys(): tip += "'" + unicode(key) + "' : '" + unicode( info[key]) + "'<br/>" item.setToolTip(1, tip) icon = fa.maps.preview(info['mapname']) item.setToolTip(0, fa.maps.getDisplayName(info['mapname'])) if not icon: self.client.downloader.downloadMap(item.info['mapname'], item, True) icon = util.icon("games/unknown_map.png") item.setText( 0, time.strftime("%H:%M", time.localtime(item.info['game_time']))) item.setTextColor( 0, QtGui.QColor(client.instance.getColor("default"))) item.setIcon(0, icon) item.setText(1, info['title']) item.setTextColor(1, QtGui.QColor(client.instance.getColor("player"))) item.setText(2, info['featured_mod']) item.setTextAlignment(2, QtCore.Qt.AlignCenter) if not info['teams']: item.setDisabled(True) # This game is the game the player is currently in mygame = False # Create player entries for all the live players in a match for team in info['teams']: if team == "-1": #skip observers, they don't seem to stream livereplays continue for player in info['teams'][team]: playeritem = QtGui.QTreeWidgetItem() playeritem.setText(0, player) url = QtCore.QUrl() url.setScheme("faflive") url.setHost("lobby.faforever.com") url.setPath( str(info["uid"]) + "/" + player + ".SCFAreplay") url.addQueryItem("map", info["mapname"]) url.addQueryItem("mod", info["featured_mod"]) playeritem.url = url if client.instance.login == player: mygame = True item.setTextColor( 1, QtGui.QColor(client.instance.getColor("self"))) playeritem.setTextColor( 0, QtGui.QColor(client.instance.getColor("self"))) playeritem.setToolTip(0, url.toString()) playeritem.setIcon(0, util.icon("replays/replay.png")) elif client.instance.isFriend(player): if not mygame: item.setTextColor( 1, QtGui.QColor( client.instance.getColor("friend"))) playeritem.setTextColor( 0, QtGui.QColor(client.instance.getColor("friend"))) playeritem.setToolTip(0, url.toString()) playeritem.setIcon(0, util.icon("replays/replay.png")) elif client.instance.isPlayer(player): playeritem.setTextColor( 0, QtGui.QColor(client.instance.getColor("player"))) playeritem.setToolTip(0, url.toString()) playeritem.setIcon(0, util.icon("replays/replay.png")) else: playeritem.setTextColor( 0, QtGui.QColor(client.instance.getColor("default"))) playeritem.setDisabled(True) item.addChild(playeritem) self.liveTree.setFirstItemColumnSpanned(playeritem, True) elif info['state'] == "closed": if info['uid'] in self.games: self.liveTree.takeTopLevelItem( self.liveTree.indexOfTopLevelItem(self.games[info['uid']])) @QtCore.pyqtSlot(QtGui.QTreeWidgetItem) def liveTreePressed(self, item): if QtGui.QApplication.mouseButtons() != QtCore.Qt.RightButton: return if self.liveTree.indexOfTopLevelItem(item) != -1: item.setExpanded(True) return menu = QtGui.QMenu(self.liveTree) # Actions for Games and Replays actionReplay = QtGui.QAction("Replay in FA", menu) actionLink = QtGui.QAction("Copy Link", menu) # Adding to menu menu.addAction(actionReplay) menu.addAction(actionLink) # Triggers actionReplay.triggered.connect( lambda: self.liveTreeDoubleClicked(item, 0)) actionLink.triggered.connect( lambda: QtGui.QApplication.clipboard().setText(item.toolTip(0))) # Adding to menu menu.addAction(actionReplay) menu.addAction(actionLink) #Finally: Show the popup menu.popup(QtGui.QCursor.pos()) @QtCore.pyqtSlot(QtGui.QListWidgetItem) def myTreePressed(self, item): if QtGui.QApplication.mouseButtons() != QtCore.Qt.RightButton: return if item.isDisabled(): return if self.myTree.indexOfTopLevelItem(item) != -1: return menu = QtGui.QMenu(self.myTree) # Actions for Games and Replays actionReplay = QtGui.QAction("Replay", menu) actionExplorer = QtGui.QAction("Show in Explorer", menu) # Adding to menu menu.addAction(actionReplay) menu.addAction(actionExplorer) # Triggers actionReplay.triggered.connect( lambda: self.myTreeDoubleClicked(item, 0)) actionExplorer.triggered.connect( lambda: util.showInExplorer(item.filename)) # Adding to menu menu.addAction(actionReplay) menu.addAction(actionExplorer) #Finally: Show the popup menu.popup(QtGui.QCursor.pos()) @QtCore.pyqtSlot(QtGui.QTreeWidgetItem, int) def myTreeDoubleClicked(self, item, column): if item.isDisabled(): return if self.myTree.indexOfTopLevelItem(item) == -1: fa.exe.replay(item.filename) @QtCore.pyqtSlot(QtGui.QTreeWidgetItem, int) def liveTreeDoubleClicked(self, item, column): ''' This slot launches a live replay from eligible items in liveTree ''' if item.isDisabled(): return if self.liveTree.indexOfTopLevelItem(item) == -1: # Notify other modules that we're watching a replay self.client.viewingReplay.emit(item.url) fa.exe.replay(item.url) def connectToModVault(self): ''' connect to the replay vault server''' if self.replayVaultSocket.state( ) != QtNetwork.QAbstractSocket.ConnectedState and self.replayVaultSocket.state( ) != QtNetwork.QAbstractSocket.ConnectingState: self.replayVaultSocket.connectToHost(self.HOST, self.SOCKET) def send(self, message): data = json.dumps(message) logger.debug("Outgoing JSON Message: " + data) self.writeToServer(data) @QtCore.pyqtSlot() def readDataFromServer(self): ins = QtCore.QDataStream(self.replayVaultSocket) ins.setVersion(QtCore.QDataStream.Qt_4_2) while ins.atEnd() == False: if self.blockSize == 0: if self.replayVaultSocket.bytesAvailable() < 4: return self.blockSize = ins.readUInt32() if self.replayVaultSocket.bytesAvailable() < self.blockSize: return action = ins.readQString() self.process(action, ins) self.blockSize = 0 def process(self, action, stream): logger.debug("Replay Vault Server: " + action) self.receiveJSON(action, stream) def receiveJSON(self, data_string, stream): ''' A fairly pythonic way to process received strings as JSON messages. ''' message = json.loads(data_string) cmd = "handle_" + message['command'] if hasattr(self.client, cmd): getattr(self.client, cmd)(message) self.replayVaultSocket.disconnectFromHost() def writeToServer(self, action, *args, **kw): logger.debug( ("writeToServer(" + action + ", [" + ', '.join(args) + "])")) block = QtCore.QByteArray() out = QtCore.QDataStream(block, QtCore.QIODevice.ReadWrite) out.setVersion(QtCore.QDataStream.Qt_4_2) out.writeUInt32(0) out.writeQString(action) for arg in args: if type(arg) is IntType: out.writeInt(arg) elif isinstance(arg, basestring): out.writeQString(arg) elif type(arg) is FloatType: out.writeFloat(arg) elif type(arg) is ListType: out.writeQVariantList(arg) else: logger.warn("Uninterpreted Data Type: " + str(type(arg)) + " of value: " + str(arg)) out.writeQString(str(arg)) out.device().seek(0) out.writeUInt32(block.size() - 4) self.bytesToSend = block.size() - 4 self.replayVaultSocket.write(block) def handleServerError(self, socketError): if socketError == QtNetwork.QAbstractSocket.RemoteHostClosedError: logger.info( "Replay Server down: The server is down for maintenance, please try later." ) elif socketError == QtNetwork.QAbstractSocket.HostNotFoundError: logger.info( "Connection to Host lost. Please check the host name and port settings." ) elif socketError == QtNetwork.QAbstractSocket.ConnectionRefusedError: logger.info("The connection was refused by the peer.") else: logger.info("The following error occurred: %s." % self.replayVaultSocket.errorString()) @QtCore.pyqtSlot() def disconnected(self): logger.debug("Disconnected from server") @QtCore.pyqtSlot(QtNetwork.QAbstractSocket.SocketError) def errored(self, error): logger.error("TCP Error " + self.replayVaultSocket.errorString())
class ComicVineTalker(QObject): logo_url = "http://static.comicvine.com/bundles/comicvinesite/images/logo.png" api_key = "" @staticmethod def getRateLimitMessage(): if ComicVineTalker.api_key == "": return "Comic Vine rate limit exceeded. You should configue your own Comic Vine API key." else: return "Comic Vine rate limit exceeded. Please wait a bit." def __init__(self): QObject.__init__(self) self.api_base_url = "http://comicvine.gamespot.com/api" self.wait_for_rate_limit = False # key that is registered to comictagger default_api_key = '27431e6787042105bd3e47e169a624521f89f3a4' if ComicVineTalker.api_key == "": self.api_key = default_api_key else: self.api_key = ComicVineTalker.api_key self.cv_headers = {'User-Agent': 'ComicTagger ' + str(ctversion.version) + ' [' + ctversion.fork + ' / ' + ctversion.fork_tag + ']'} self.log_func = None def setLogFunc(self, log_func): self.log_func = log_func def writeLog(self, text): if self.log_func is None: # sys.stdout.write(text.encode(errors='replace')) # sys.stdout.flush() print >> sys.stderr, text else: self.log_func(text) def parseDateStr(self, date_str): day = None month = None year = None if date_str is not None: parts = date_str.split('-') year = parts[0] if len(parts) > 1: month = parts[1] if len(parts) > 2: day = parts[2] return day, month, year def testKey(self, key): test_url = self.api_base_url + "/issue/1/?api_key=" + \ key + "&format=json&field_list=name" r = requests.get(test_url, headers=self.cv_headers) cv_response = r.json() # Bogus request, but if the key is wrong, you get error 100: "Invalid # API Key" return cv_response['status_code'] != 100 """ Get the contect from the CV server. If we're in "wait mode" and status code is a rate limit error sleep for a bit and retry. """ def getCVContent(self, url): total_time_waited = 0 limit_wait_time = 1 counter = 0 wait_times = [1, 2, 3, 4] while True: cv_response = self.getUrlContent(url) if self.wait_for_rate_limit and cv_response[ 'status_code'] == ComicVineTalkerException.RateLimit: self.writeLog( "Rate limit encountered. Waiting for {0} minutes\n".format(limit_wait_time)) time.sleep(limit_wait_time * 60) total_time_waited += limit_wait_time limit_wait_time = wait_times[counter] if counter < 3: counter += 1 # don't wait much more than 20 minutes if total_time_waited < 20: continue if cv_response['status_code'] != 1: self.writeLog( "Comic Vine query failed with error #{0}: [{1}]. \n".format( cv_response['status_code'], cv_response['error'])) raise ComicVineTalkerException( cv_response['status_code'], cv_response['error']) else: # it's all good break return cv_response def getUrlContent(self, url): # connect to server: # if there is a 500 error, try a few more times before giving up # any other error, just bail # print "ATB---", url for tries in range(3): try: r = requests.get(url, headers=self.cv_headers) return r.json() except Exception as e: ecode = type(e).__name__ if ecode == 500: self.writeLog("Try #{0}: ".format(tries + 1)) time.sleep(1) self.writeLog(str(e) + "\n") if ecode != 500: break except Exception as e: self.writeLog(str(e) + "\n") raise ComicVineTalkerException( ComicVineTalkerException.Network, "Network Error!") raise ComicVineTalkerException( ComicVineTalkerException.Unknown, "Error on Comic Vine server") def searchForSeries(self, series_name, callback=None, refresh_cache=False): # remove cruft from the search string series_name = utils.removearticles(series_name).lower().strip() # before we search online, look in our cache, since we might have # done this same search recently cvc = ComicVineCacher() if not refresh_cache: cached_search_results = cvc.get_search_results(series_name) if len(cached_search_results) > 0: return cached_search_results original_series_name = series_name # We need to make the series name into an "AND"ed query list query_word_list = series_name.split() and_list = ['AND'] * (len(query_word_list) - 1) and_list.append('') # zipper up the two lists query_list = zip(query_word_list, and_list) # flatten the list query_list = [item for sublist in query_list for item in sublist] # convert back to a string query_string = " ".join(query_list).strip() # print "Query string = ", query_string query_string = urllib.quote_plus(query_string.encode("utf-8")) search_url = self.api_base_url + "/search/?api_key=" + self.api_key + "&format=json&resources=volume&query=" + \ query_string + \ "&field_list=name,id,start_year,publisher,image,description,count_of_issues" cv_response = self.getCVContent(search_url + "&page=1") search_results = list() # see http://api.comicvine.com/documentation/#handling_responses limit = cv_response['limit'] current_result_count = cv_response['number_of_page_results'] total_result_count = cv_response['number_of_total_results'] if callback is None: self.writeLog( "Found {0} of {1} results\n".format( cv_response['number_of_page_results'], cv_response['number_of_total_results'])) search_results.extend(cv_response['results']) page = 1 if callback is not None: callback(current_result_count, total_result_count) # see if we need to keep asking for more pages... while (current_result_count < total_result_count): if callback is None: self.writeLog( "getting another page of results {0} of {1}...\n".format( current_result_count, total_result_count)) page += 1 cv_response = self.getCVContent(search_url + "&page=" + str(page)) search_results.extend(cv_response['results']) current_result_count += cv_response['number_of_page_results'] if callback is not None: callback(current_result_count, total_result_count) # for record in search_results: #print(u"{0}: {1} ({2})".format(record['id'], record['name'] , record['start_year'])) # print(record) #record['count_of_issues'] = record['count_of_isssues'] #print(u"{0}: {1} ({2})".format(search_results['results'][0]['id'], search_results['results'][0]['name'] , search_results['results'][0]['start_year'])) # cache these search results cvc.add_search_results(original_series_name, search_results) return search_results def fetchVolumeData(self, series_id): # before we search online, look in our cache, since we might already # have this info cvc = ComicVineCacher() cached_volume_result = cvc.get_volume_info(series_id) if cached_volume_result is not None: return cached_volume_result volume_url = self.api_base_url + "/volume/" + CVTypeID.Volume + "-" + \ str(series_id) + "/?api_key=" + self.api_key + \ "&field_list=name,id,start_year,publisher,count_of_issues&format=json" cv_response = self.getCVContent(volume_url) volume_results = cv_response['results'] cvc.add_volume_info(volume_results) return volume_results def fetchIssuesByVolume(self, series_id): # before we search online, look in our cache, since we might already # have this info cvc = ComicVineCacher() cached_volume_issues_result = cvc.get_volume_issues_info(series_id) if cached_volume_issues_result is not None: return cached_volume_issues_result #--------------------------------- issues_url = self.api_base_url + "/issues/" + "?api_key=" + self.api_key + "&filter=volume:" + \ str(series_id) + \ "&field_list=id,volume,issue_number,name,image,cover_date,site_detail_url,description&format=json" cv_response = self.getCVContent(issues_url) #------------------------------------ limit = cv_response['limit'] current_result_count = cv_response['number_of_page_results'] total_result_count = cv_response['number_of_total_results'] # print "ATB total_result_count", total_result_count #print("ATB Found {0} of {1} results".format(cv_response['number_of_page_results'], cv_response['number_of_total_results'])) volume_issues_result = cv_response['results'] page = 1 offset = 0 # see if we need to keep asking for more pages... while (current_result_count < total_result_count): #print("ATB getting another page of issue results {0} of {1}...".format(current_result_count, total_result_count)) page += 1 offset += cv_response['number_of_page_results'] # print issues_url+ "&offset="+str(offset) cv_response = self.getCVContent( issues_url + "&offset=" + str(offset)) volume_issues_result.extend(cv_response['results']) current_result_count += cv_response['number_of_page_results'] self.repairUrls(volume_issues_result) cvc.add_volume_issues_info(series_id, volume_issues_result) return volume_issues_result def fetchIssuesByVolumeIssueNumAndYear( self, volume_id_list, issue_number, year): volume_filter = "volume:" for vid in volume_id_list: volume_filter += str(vid) + "|" year_filter = "" if year is not None and str(year).isdigit(): year_filter = ",cover_date:{0}-1-1|{1}-1-1".format( year, int(year) + 1) issue_number = urllib.quote_plus(unicode(issue_number).encode("utf-8")) filter = "&filter=" + volume_filter + \ year_filter + ",issue_number:" + issue_number issues_url = self.api_base_url + "/issues/" + "?api_key=" + self.api_key + filter + \ "&field_list=id,volume,issue_number,name,image,cover_date,site_detail_url,description&format=json" cv_response = self.getCVContent(issues_url) #------------------------------------ limit = cv_response['limit'] current_result_count = cv_response['number_of_page_results'] total_result_count = cv_response['number_of_total_results'] # print "ATB total_result_count", total_result_count #print("ATB Found {0} of {1} results\n".format(cv_response['number_of_page_results'], cv_response['number_of_total_results'])) filtered_issues_result = cv_response['results'] page = 1 offset = 0 # see if we need to keep asking for more pages... while (current_result_count < total_result_count): #print("ATB getting another page of issue results {0} of {1}...\n".format(current_result_count, total_result_count)) page += 1 offset += cv_response['number_of_page_results'] # print issues_url+ "&offset="+str(offset) cv_response = self.getCVContent( issues_url + "&offset=" + str(offset)) filtered_issues_result.extend(cv_response['results']) current_result_count += cv_response['number_of_page_results'] self.repairUrls(filtered_issues_result) return filtered_issues_result def fetchIssueData(self, series_id, issue_number, settings): volume_results = self.fetchVolumeData(series_id) issues_list_results = self.fetchIssuesByVolume(series_id) found = False for record in issues_list_results: if IssueString(issue_number).asString() is None: issue_number = 1 if IssueString(record['issue_number']).asString().lower() == IssueString( issue_number).asString().lower(): found = True break if (found): issue_url = self.api_base_url + "/issue/" + CVTypeID.Issue + "-" + \ str(record['id']) + "/?api_key=" + \ self.api_key + "&format=json" cv_response = self.getCVContent(issue_url) issue_results = cv_response['results'] else: return None # Now, map the Comic Vine data to generic metadata return self.mapCVDataToMetadata( volume_results, issue_results, settings) def fetchIssueDataByIssueID(self, issue_id, settings): issue_url = self.api_base_url + "/issue/" + CVTypeID.Issue + "-" + \ str(issue_id) + "/?api_key=" + self.api_key + "&format=json" cv_response = self.getCVContent(issue_url) issue_results = cv_response['results'] volume_results = self.fetchVolumeData(issue_results['volume']['id']) # Now, map the Comic Vine data to generic metadata md = self.mapCVDataToMetadata(volume_results, issue_results, settings) md.isEmpty = False return md def mapCVDataToMetadata(self, volume_results, issue_results, settings): # Now, map the Comic Vine data to generic metadata metadata = GenericMetadata() metadata.series = issue_results['volume']['name'] num_s = IssueString(issue_results['issue_number']).asString() metadata.issue = num_s metadata.title = issue_results['name'] metadata.publisher = volume_results['publisher']['name'] metadata.day, metadata.month, metadata.year = self.parseDateStr( issue_results['cover_date']) #metadata.issueCount = volume_results['count_of_issues'] metadata.comments = self.cleanup_html( issue_results['description'], settings.remove_html_tables) if settings.use_series_start_as_volume: metadata.volume = volume_results['start_year'] metadata.notes = "Tagged with the {0} fork of ComicTagger {1} using info from Comic Vine on {2}. [Issue ID {3}]".format( ctversion.fork, ctversion.version, datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), issue_results['id']) #metadata.notes += issue_results['site_detail_url'] metadata.webLink = issue_results['site_detail_url'] person_credits = issue_results['person_credits'] for person in person_credits: if 'role' in person: roles = person['role'].split(',') for role in roles: # can we determine 'primary' from CV?? metadata.addCredit( person['name'], role.title().strip(), False) character_credits = issue_results['character_credits'] character_list = list() for character in character_credits: character_list.append(character['name']) metadata.characters = utils.listToString(character_list) team_credits = issue_results['team_credits'] team_list = list() for team in team_credits: team_list.append(team['name']) metadata.teams = utils.listToString(team_list) location_credits = issue_results['location_credits'] location_list = list() for location in location_credits: location_list.append(location['name']) metadata.locations = utils.listToString(location_list) story_arc_credits = issue_results['story_arc_credits'] arc_list = [] for arc in story_arc_credits: arc_list.append(arc['name']) if len(arc_list) > 0: metadata.storyArc = utils.listToString(arc_list) return metadata def cleanup_html(self, string, remove_html_tables): """ converter = html2text.HTML2Text() #converter.emphasis_mark = '*' #converter.ignore_links = True converter.body_width = 0 print(html2text.html2text(string)) return string #return converter.handle(string) """ if string is None: return "" # find any tables soup = BeautifulSoup(string, "html.parser") tables = soup.findAll('table') # remove all newlines first string = string.replace("\n", "") # put in our own string = string.replace("<br>", "\n") string = string.replace("</p>", "\n\n") string = string.replace("<h4>", "*") string = string.replace("</h4>", "*\n") # remove the tables p = re.compile(r'<table[^<]*?>.*?<\/table>') if remove_html_tables: string = p.sub('', string) string = string.replace("*List of covers and their creators:*", "") else: string = p.sub('{}', string) # now strip all other tags p = re.compile(r'<[^<]*?>') newstring = p.sub('', string) newstring = newstring.replace(' ', ' ') newstring = newstring.replace('&', '&') newstring = newstring.strip() if not remove_html_tables: # now rebuild the tables into text from BSoup try: table_strings = [] for table in tables: rows = [] hdrs = [] col_widths = [] for hdr in table.findAll('th'): item = hdr.string.strip() hdrs.append(item) col_widths.append(len(item)) rows.append(hdrs) for row in table.findAll('tr'): cols = [] col = row.findAll('td') i = 0 for c in col: item = c.string.strip() cols.append(item) if len(item) > col_widths[i]: col_widths[i] = len(item) i += 1 if len(cols) != 0: rows.append(cols) # now we have the data, make it into text fmtstr = "" for w in col_widths: fmtstr += " {{:{}}}|".format(w + 1) width = sum(col_widths) + len(col_widths) * 2 print "width=", width table_text = "" counter = 0 for row in rows: table_text += fmtstr.format(*row) + "\n" if counter == 0 and len(hdrs) != 0: table_text += "-" * width + "\n" counter += 1 table_strings.append(table_text) newstring = newstring.format(*table_strings) except: # we caught an error rebuilding the table. # just bail and remove the formatting print("table parse error") newstring.replace("{}", "") return newstring def fetchIssueDate(self, issue_id): details = self.fetchIssueSelectDetails(issue_id) day, month, year = self.parseDateStr(details['cover_date']) return month, year def fetchIssueCoverURLs(self, issue_id): details = self.fetchIssueSelectDetails(issue_id) return details['image_url'], details['thumb_image_url'] def fetchIssuePageURL(self, issue_id): details = self.fetchIssueSelectDetails(issue_id) return details['site_detail_url'] def fetchIssueSelectDetails(self, issue_id): #cached_image_url,cached_thumb_url,cached_month,cached_year = self.fetchCachedIssueSelectDetails(issue_id) cached_details = self.fetchCachedIssueSelectDetails(issue_id) if cached_details['image_url'] is not None: return cached_details issue_url = self.api_base_url + "/issue/" + CVTypeID.Issue + "-" + \ str(issue_id) + "/?api_key=" + self.api_key + \ "&format=json&field_list=image,cover_date,site_detail_url" details = dict() details['image_url'] = None details['thumb_image_url'] = None details['cover_date'] = None details['site_detail_url'] = None cv_response = self.getCVContent(issue_url) details['image_url'] = cv_response['results']['image']['super_url'] details['thumb_image_url'] = cv_response[ 'results']['image']['thumb_url'] details['cover_date'] = cv_response['results']['cover_date'] details['site_detail_url'] = cv_response['results']['site_detail_url'] if details['image_url'] is not None: self.cacheIssueSelectDetails(issue_id, details['image_url'], details['thumb_image_url'], details['cover_date'], details['site_detail_url']) # print(details['site_detail_url']) return details def fetchCachedIssueSelectDetails(self, issue_id): # before we search online, look in our cache, since we might already # have this info cvc = ComicVineCacher() return cvc.get_issue_select_details(issue_id) def cacheIssueSelectDetails( self, issue_id, image_url, thumb_url, cover_date, page_url): cvc = ComicVineCacher() cvc.add_issue_select_details( issue_id, image_url, thumb_url, cover_date, page_url) def fetchAlternateCoverURLs(self, issue_id, issue_page_url): url_list = self.fetchCachedAlternateCoverURLs(issue_id) if url_list is not None: return url_list # scrape the CV issue page URL to get the alternate cover URLs resp = urllib2.urlopen(issue_page_url) content = resp.read() alt_cover_url_list = self.parseOutAltCoverUrls(content) # cache this alt cover URL list self.cacheAlternateCoverURLs(issue_id, alt_cover_url_list) return alt_cover_url_list def parseOutAltCoverUrls(self, page_html): soup = BeautifulSoup(page_html, "html.parser") alt_cover_url_list = [] # Using knowledge of the layout of the Comic Vine issue page here: # look for the divs that are in the classes 'content-pod' and # 'alt-cover' div_list = soup.find_all('div') covers_found = 0 for d in div_list: if 'class' in d: c = d['class'] if 'imgboxart' in c and 'issue-cover' in c: covers_found += 1 if covers_found != 1: alt_cover_url_list.append(d.img['src']) return alt_cover_url_list def fetchCachedAlternateCoverURLs(self, issue_id): # before we search online, look in our cache, since we might already # have this info cvc = ComicVineCacher() url_list = cvc.get_alt_covers(issue_id) if url_list is not None: return url_list else: return None def cacheAlternateCoverURLs(self, issue_id, url_list): cvc = ComicVineCacher() cvc.add_alt_covers(issue_id, url_list) #------------------------------------------------------------------------- urlFetchComplete = pyqtSignal(str, str, int) def asyncFetchIssueCoverURLs(self, issue_id): self.issue_id = issue_id details = self.fetchCachedIssueSelectDetails(issue_id) if details['image_url'] is not None: self.urlFetchComplete.emit( details['image_url'], details['thumb_image_url'], self.issue_id) return issue_url = self.api_base_url + "/issue/" + CVTypeID.Issue + "-" + \ str(issue_id) + "/?api_key=" + self.api_key + \ "&format=json&field_list=image,cover_date,site_detail_url" self.nam = QNetworkAccessManager() self.nam.finished.connect(self.asyncFetchIssueCoverURLComplete) self.nam.get(QNetworkRequest(QUrl(issue_url))) def asyncFetchIssueCoverURLComplete(self, reply): # read in the response data = reply.readAll() try: cv_response = json.loads(str(data)) except: print >> sys.stderr, "Comic Vine query failed to get JSON data" print >> sys.stderr, str(data) return if cv_response['status_code'] != 1: print >> sys.stderr, "Comic Vine query failed with error: [{0}]. ".format( cv_response['error']) return image_url = cv_response['results']['image']['super_url'] thumb_url = cv_response['results']['image']['thumb_url'] cover_date = cv_response['results']['cover_date'] page_url = cv_response['results']['site_detail_url'] self.cacheIssueSelectDetails( self.issue_id, image_url, thumb_url, cover_date, page_url) self.urlFetchComplete.emit(image_url, thumb_url, self.issue_id) altUrlListFetchComplete = pyqtSignal(list, int) def asyncFetchAlternateCoverURLs(self, issue_id, issue_page_url): # This async version requires the issue page url to be provided! self.issue_id = issue_id url_list = self.fetchCachedAlternateCoverURLs(issue_id) if url_list is not None: self.altUrlListFetchComplete.emit(url_list, int(self.issue_id)) return self.nam = QNetworkAccessManager() self.nam.finished.connect(self.asyncFetchAlternateCoverURLsComplete) self.nam.get(QNetworkRequest(QUrl(str(issue_page_url)))) def asyncFetchAlternateCoverURLsComplete(self, reply): # read in the response html = str(reply.readAll()) alt_cover_url_list = self.parseOutAltCoverUrls(html) # cache this alt cover URL list self.cacheAlternateCoverURLs(self.issue_id, alt_cover_url_list) self.altUrlListFetchComplete.emit( alt_cover_url_list, int(self.issue_id)) def repairUrls(self, issue_list): # make sure there are URLs for the image fields for issue in issue_list: if issue['image'] is None: issue['image'] = dict() issue['image']['super_url'] = ComicVineTalker.logo_url issue['image']['thumb_url'] = ComicVineTalker.logo_url
class ConnexionOAPI(object): """ Manage connexion to the overpass API """ def __init__(self, url="http://overpass-api.de/api/", output=None): """ Constructor @param url:URL of OverPass @type url:str @param output:Output desired (XML or JSON) @type output:str """ if not url: url = "http://overpass-api.de/api/" self.__url = url self.data = None if output not in (None, "json", "xml"): raise OutPutFormatException self.__output = output self.network = QNetworkAccessManager() self.network_reply = None self.loop = None def query(self, query): """ Make a query to the overpass @param query:Query to execute @type query:str @raise OverpassBadRequestException,NetWorkErrorException, OverpassTimeoutException @return: the result of the query @rtype: str """ url_query = QUrl(self.__url + 'interpreter') # The output format can be forced (JSON or XML) if self.__output: query = re.sub(r'output="[a-z]*"', 'output="' + self.__output + '"', query) query = re.sub(r'\[out:[a-z]*', '[out:' + self.__output, query) # noinspection PyCallByClass encoded_query = QUrl.toPercentEncoding(query) url_query.addEncodedQueryItem('data', encoded_query) url_query.addQueryItem('info', 'QgisQuickOSMPlugin') url_query.setPort(80) proxy = get_proxy() if proxy: self.network.setProxy(proxy) request = QNetworkRequest(url_query) request.setRawHeader("User-Agent", "QuickOSM") self.network_reply = self.network.get(request) self.loop = QEventLoop() self.network.finished.connect(self._end_of_request) self.loop.exec_() if self.network_reply.error() == QNetworkReply.NoError: timeout = '<remark> runtime error: Query timed out in "[a-z]+" ' \ 'at line [\d]+ after ([\d]+) seconds. </remark>' if re.search(timeout, self.data): raise OverpassTimeoutException else: return self.data elif self.network_reply.error() == QNetworkReply.UnknownContentError: raise OverpassBadRequestException else: raise NetWorkErrorException(suffix="Overpass API") def _end_of_request(self): self.data = self.network_reply.readAll() self.loop.quit() def get_file_from_query(self, query): """ Make a query to the overpass and put the result in a temp file @param query:Query to execute @type query:str @return: temporary file path @rtype: str """ query = self.query(query) tf = tempfile.NamedTemporaryFile(delete=False, suffix=".osm") tf.write(query) name_file = tf.name tf.flush() tf.close() return name_file def get_timestamp(self): """ Get the timestamp of the OSM data on the server @return: Timestamp @rtype: str """ url_query = self.__url + 'timestamp' try: return urllib2.urlopen(url=url_query).read() except urllib2.HTTPError as e: if e.code == 400: raise OverpassBadRequestException def is_valid(self): """ Try if the url is valid, NOT TESTED YET """ url_query = self.__url + 'interpreter' try: urllib2.urlopen(url=url_query) return True except urllib2.HTTPError: return False
class CartoDBApi(QObject): fetchContent = pyqtSignal(object) progress = pyqtSignal(int, int) error = pyqtSignal(object) def __init__(self, cartodbUser, apiKey, multiuser=False, hostname='cartonico.datapy.info'): QObject.__init__(self) self.multiuser = multiuser self.apiKey = apiKey self.cartodbUser = cartodbUser self.hostname = hostname self.apiUrl = "https://{}.{}/api/v1/".format(cartodbUser, hostname) self.returnDict = True self.manager = QNetworkAccessManager() self.manager.finished.connect(self.returnFetchContent) def _getRequest(self, url): request = QNetworkRequest(url) request.setRawHeader("Content-Type", "application/json") request.setRawHeader('User-Agent', 'QGISCartoDB 0.2.x') return request def _createMultipart(self, data={}, files={}): multiPart = QHttpMultiPart(QHttpMultiPart.FormDataType) for key, value in data.items(): textPart = QHttpPart() textPart.setHeader(QNetworkRequest.ContentDispositionHeader, "form-data; name=\"%s\"" % key) textPart.setBody(value) multiPart.append(textPart) for key, file in files.items(): filePart = QHttpPart() # filePart.setHeader(QNetworkRequest::ContentTypeHeader, ...); fileName = QFileInfo(file.fileName()).fileName() filePart.setHeader( QNetworkRequest.ContentDispositionHeader, "form-data; name=\"%s\"; filename=\"%s\"" % (key, fileName)) filePart.setBodyDevice(file) multiPart.append(filePart) return multiPart def getUserDetails(self, returnDict=True): self.returnDict = returnDict url = QUrl( self.apiUrl + "users/{}/?api_key={}".format(self.cartodbUser, self.apiKey)) request = self._getRequest(url) reply = self.manager.get(request) loop = QEventLoop() reply.downloadProgress.connect(self.progressCB) reply.error.connect(self._error) reply.finished.connect(loop.exit) loop.exec_() def getUserTables(self, page=1, per_page=20, shared='yes', returnDict=True): self.returnDict = returnDict payload = { 'tag_name': '', 'q': '', 'page': page, 'type': '', 'exclude_shared': 'false', 'per_page': per_page, 'tags': '', 'shared': shared, 'locked': 'false', 'only_liked': 'false', 'order': 'name', 'types': 'table' } url = QUrl( self.apiUrl + "viz?api_key={}&{}".format(self.apiKey, urllib.urlencode(payload))) request = self._getRequest(url) reply = self.manager.get(request) loop = QEventLoop() reply.downloadProgress.connect(self.progressCB) reply.error.connect(self._error) reply.finished.connect(loop.exit) loop.exec_() def getDataFromTable(self, sql, returnDict=True): self.returnDict = returnDict apiUrl = 'http://{}.{}/api/v2/sql?api_key={}&format=GeoJSON&q={}'.format( self.cartodbUser, self.hostname, self.apiKey, sql) url = QUrl(apiUrl) request = self._getRequest(url) reply = self.manager.get(request) loop = QEventLoop() reply.downloadProgress.connect(self.progressCB) reply.error.connect(self._error) reply.finished.connect(loop.exit) loop.exec_() def download(self, sql): apiUrl = 'http://{}.{}/api/v2/sql?api_key={}&format=spatialite&q={}'.format( self.cartodbUser, self.hostname, self.apiKey, sql) url = QUrl(apiUrl) request = self._getRequest(url) def finished(reply): tempdir = tempfile.tempdir if tempdir is None: tempdir = tempfile.mkdtemp() tf = tempfile.NamedTemporaryFile(delete=False) sqlite = QFile(tf.name) tf.close() if (sqlite.open(QIODevice.WriteOnly)): sqlite.write(reply.readAll()) sqlite.close() self.fetchContent.emit(tf.name) else: self.error.emit('Error saving downloaded file') manager = QNetworkAccessManager() manager.finished.connect(finished) reply = manager.get(request) loop = QEventLoop() reply.downloadProgress.connect(self.progressCB) reply.error.connect(self._error) reply.finished.connect(loop.exit) loop.exec_() def upload(self, filePath, returnDict=True): self.returnDict = returnDict file = QFile(filePath) file.open(QFile.ReadOnly) url = QUrl(self.apiUrl + "imports/?api_key={}".format(self.apiKey)) files = {'file': file} multipart = self._createMultipart(files=files) request = QNetworkRequest(url) request.setHeader( QNetworkRequest.ContentTypeHeader, 'multipart/form-data; boundary=%s' % multipart.boundary()) request.setRawHeader('User-Agent', 'QGISCartoDB 0.2.x') reply = self.manager.post(request, multipart) loop = QEventLoop() reply.uploadProgress.connect(self.progressCB) reply.error.connect(self._error) reply.finished.connect(loop.exit) loop.exec_() def checkUploadStatus(self, id, returnDict=True): self.returnDict = returnDict url = QUrl(self.apiUrl + "imports/{}/?api_key={}".format(id, self.apiKey)) request = self._getRequest(url) reply = self.manager.get(request) loop = QEventLoop() reply.downloadProgress.connect(self.progressCB) reply.error.connect(self._error) reply.finished.connect(loop.exit) loop.exec_() def createVizFromTable(self, table, name, description='', returnDict=True): self.returnDict = returnDict payload = { 'type': 'derived', 'name': name, 'title': name, 'description': description, 'tags': ['QGISCartoDB'], "tables": [table] } url = QUrl(self.apiUrl + "viz/?api_key={}".format(self.apiKey)) request = self._getRequest(url) reply = self.manager.post(request, json.dumps(payload)) loop = QEventLoop() reply.downloadProgress.connect(self.progressCB) reply.error.connect(self._error) reply.finished.connect(loop.exit) loop.exec_() def updateViz(self, viz, returnDict=True): self.returnDict = returnDict url = QUrl(self.apiUrl + "viz/{}?api_key={}".format(viz['id'], self.apiKey)) request = self._getRequest(url) reply = self.manager.put(request, json.dumps(viz)) loop = QEventLoop() reply.downloadProgress.connect(self.progressCB) reply.error.connect(self._error) reply.finished.connect(loop.exit) loop.exec_() def addLayerToMap(self, mapId, layer, returnDict=True): self.returnDict = returnDict url = QUrl(self.apiUrl + "maps/{}/layers?api_key={}".format(mapId, self.apiKey)) request = self._getRequest(url) reply = self.manager.post(request, json.dumps(layer)) loop = QEventLoop() reply.downloadProgress.connect(self.progressCB) reply.error.connect(self._error) reply.finished.connect(loop.exit) loop.exec_() def updateLayerInMap(self, mapId, layer, returnDict=True): self.returnDict = returnDict url = QUrl(self.apiUrl + "maps/{}/layers/{}?api_key={}".format( mapId, layer['id'], self.apiKey)) request = self._getRequest(url) reply = self.manager.put(request, json.dumps(layer)) loop = QEventLoop() reply.downloadProgress.connect(self.progressCB) reply.error.connect(self._error) reply.finished.connect(loop.exit) loop.exec_() def getLayersMap(self, mapId, returnDict=True): self.returnDict = returnDict url = QUrl(self.apiUrl + "maps/{}/layers?api_key={}".format(mapId, self.apiKey)) request = self._getRequest(url) reply = self.manager.get(request) loop = QEventLoop() reply.downloadProgress.connect(self.progressCB) reply.error.connect(self._error) reply.finished.connect(loop.exit) loop.exec_() def progressCB(self, breceived, btotal): self.progress.emit(breceived, btotal) def returnFetchContent(self, reply): response = str(reply.readAll()) # qDebug('Response:' + response) # qDebug('Error: ' + str(reply.error())) # qDebug('Status: ' + str(reply.rawHeader('Location'))) if reply.rawHeader('Location') == 'http://cartodb.com/noneuser.html' or \ reply.rawHeader('Location') == 'http://carto.com/noneuser.html': response = '{"error": "User not found"}' elif reply.error() == QNetworkReply.AuthenticationRequiredError: response = '{"error": "Confirm user credentials"}' if self.returnDict: try: self.fetchContent.emit(json.loads(response)) except ValueError as e: qDebug('Error loading json. {}'.format(response)) response = '{"error": "Error loading JSON data"}' self.fetchContent.emit(json.loads(response)) else: self.fetchContent.emit(response) def _error(self, error): qDebug('Error: ' + str(error)) self.error.emit(error)
class tutorialsWidget(FormClass, BaseClass): def __init__(self, client, *args, **kwargs): BaseClass.__init__(self, *args, **kwargs) self.setupUi(self) self.client = client self.client.tutorialsTab.layout().addWidget(self) self.sections = {} self.tutorials = {} self.client.tutorialsInfo.connect(self.processTutorialInfo) logger.info("Tutorials instantiated.") def finishReplay(self, reply): if reply.error() != QNetworkReply.NoError: QtGui.QMessageBox.warning(self, "Network Error", reply.errorString()) else: filename = os.path.join(util.CACHE_DIR, str("tutorial.fafreplay")) replay = QtCore.QFile(filename) replay.open(QtCore.QIODevice.WriteOnly | QtCore.QIODevice.Text) replay.write(reply.readAll()) replay.close() fa.replay(filename, True) def tutorialClicked(self, item): self.nam = QNetworkAccessManager() self.nam.finished.connect(self.finishReplay) self.nam.get(QNetworkRequest(QtCore.QUrl(item.url))) def processTutorialInfo(self, message): ''' Two type here : section or tutorials. Sections are defining the differents type of tutorials ''' logger.debug("Processing TutorialInfo") if "section" in message : section = message["section"] desc = message["description"] area = util.loadUi("tutorials/tutorialarea.ui") tabIndex = self.addTab(area, section) self.setTabToolTip(tabIndex, desc) # Set up the List that contains the tutorial items area.listWidget.setItemDelegate(TutorialItemDelegate(self)) area.listWidget.itemDoubleClicked.connect(self.tutorialClicked) self.sections[section] = area.listWidget elif "tutorial" in message : tutorial = message["tutorial"] section = message["tutorial_section"] if section in self.sections : self.tutorials[tutorial] = TutorialItem(tutorial) self.tutorials[tutorial].update(message, self.client) self.sections[section].addItem(self.tutorials[tutorial])
class OnlineUpdater(QWidgetComponentFactory(uiFile=COMPONENT_UI_FILE)): """ | Defines the :mod:`sibl_gui.components.addons.onlineUpdater.onlineUpdater` Component Interface class. | This Component provides online updating capabilities to the Application available through options exposed in the :mod:`sibl_gui.components.core.preferencesManager.preferencesManager` Component ui. """ def __init__(self, parent=None, name=None, *args, **kwargs): """ Initializes the class. :param parent: Object parent. :type parent: QObject :param name: Component name. :type name: unicode :param \*args: Arguments. :type \*args: \* :param \*\*kwargs: Keywords arguments. :type \*\*kwargs: \*\* """ LOGGER.debug("> Initializing '{0}()' class.".format( self.__class__.__name__)) super(OnlineUpdater, self).__init__(parent, name, *args, **kwargs) # --- Setting class attributes. --- self.deactivatable = True self.__engine = None self.__settings = None self.__settingsSection = None self.__preferencesManager = None self.__templatesOutliner = None self.__locationsBrowser = None self.__ioDirectory = "remote/" self.__repositoryUrl = REPOSITORY_URL self.__releasesFileUrl = "sIBL_GUI_Releases.rc" self.__networkAccessManager = None self.__releasesFileReply = None self.__remoteUpdater = None self.__reportUpdateStatus = None #****************************************************************************************************************** #*** Attributes properties. #****************************************************************************************************************** @property def engine(self): """ Property for **self.__engine** attribute. :return: self.__engine. :rtype: QObject """ return self.__engine @engine.setter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def engine(self, value): """ Setter for **self.__engine** attribute. :param value: Attribute value. :type value: QObject """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format( self.__class__.__name__, "engine")) @engine.deleter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def engine(self): """ Deleter for **self.__engine** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format( self.__class__.__name__, "engine")) @property def settings(self): """ Property for **self.__settings** attribute. :return: self.__settings. :rtype: QSettings """ return self.__settings @settings.setter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def settings(self, value): """ Setter for **self.__settings** attribute. :param value: Attribute value. :type value: QSettings """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format( self.__class__.__name__, "settings")) @settings.deleter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def settings(self): """ Deleter for **self.__settings** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format( self.__class__.__name__, "settings")) @property def settingsSection(self): """ Property for **self.__settingsSection** attribute. :return: self.__settingsSection. :rtype: unicode """ return self.__settingsSection @settingsSection.setter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def settingsSection(self, value): """ Setter for **self.__settingsSection** attribute. :param value: Attribute value. :type value: unicode """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format( self.__class__.__name__, "settingsSection")) @settingsSection.deleter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def settingsSection(self): """ Deleter for **self.__settingsSection** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format( self.__class__.__name__, "settingsSection")) @property def preferencesManager(self): """ Property for **self.__preferencesManager** attribute. :return: self.__preferencesManager. :rtype: QWidget """ return self.__preferencesManager @preferencesManager.setter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def preferencesManager(self, value): """ Setter for **self.__preferencesManager** attribute. :param value: Attribute value. :type value: QWidget """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format( self.__class__.__name__, "preferencesManager")) @preferencesManager.deleter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def preferencesManager(self): """ Deleter for **self.__preferencesManager** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format( self.__class__.__name__, "preferencesManager")) @property def templatesOutliner(self): """ Property for **self.__templatesOutliner** attribute. :return: self.__templatesOutliner. :rtype: QWidget """ return self.__templatesOutliner @templatesOutliner.setter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def templatesOutliner(self, value): """ Setter for **self.__templatesOutliner** attribute. :param value: Attribute value. :type value: QWidget """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format( self.__class__.__name__, "templatesOutliner")) @templatesOutliner.deleter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def templatesOutliner(self): """ Deleter for **self.__templatesOutliner** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format( self.__class__.__name__, "templatesOutliner")) @property def locationsBrowser(self): """ Property for **self.__locationsBrowser** attribute. :return: self.__locationsBrowser. :rtype: QWidget """ return self.__locationsBrowser @locationsBrowser.setter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def locationsBrowser(self, value): """ Setter for **self.__locationsBrowser** attribute. :param value: Attribute value. :type value: QWidget """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format( self.__class__.__name__, "locationsBrowser")) @locationsBrowser.deleter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def locationsBrowser(self): """ Deleter for **self.__locationsBrowser** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format( self.__class__.__name__, "locationsBrowser")) @property def ioDirectory(self): """ Property for **self.__ioDirectory** attribute. :return: self.__ioDirectory. :rtype: unicode """ return self.__ioDirectory @ioDirectory.setter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def ioDirectory(self, value): """ Setter for **self.__ioDirectory** attribute. :param value: Attribute value. :type value: unicode """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format( self.__class__.__name__, "ioDirectory")) @ioDirectory.deleter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def ioDirectory(self): """ Deleter for **self.__ioDirectory** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format( self.__class__.__name__, "ioDirectory")) @property def repositoryUrl(self): """ Property for **self.__repositoryUrl** attribute. :return: self.__repositoryUrl. :rtype: unicode """ return self.__repositoryUrl @repositoryUrl.setter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def repositoryUrl(self, value): """ Setter for **self.__repositoryUrl** attribute. :param value: Attribute value. :type value: unicode """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format( self.__class__.__name__, "repositoryUrl")) @repositoryUrl.deleter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def repositoryUrl(self): """ Deleter for **self.__repositoryUrl** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format( self.__class__.__name__, "repositoryUrl")) @property def releasesFileUrl(self): """ Property for **self.__releasesFileUrl** attribute. :return: self.__releasesFileUrl. :rtype: unicode """ return self.__releasesFileUrl @releasesFileUrl.setter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def releasesFileUrl(self, value): """ Setter for **self.__releasesFileUrl** attribute. :param value: Attribute value. :type value: unicode """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format( self.__class__.__name__, "releasesFileUrl")) @releasesFileUrl.deleter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def releasesFileUrl(self): """ Deleter for **self.__releasesFileUrl** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format( self.__class__.__name__, "releasesFileUrl")) @property def networkAccessManager(self): """ Property for **self.__networkAccessManager** attribute. :return: self.__networkAccessManager. :rtype: QNetworkAccessManager """ return self.__networkAccessManager @networkAccessManager.setter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def networkAccessManager(self, value): """ Setter for **self.__networkAccessManager** attribute. :param value: Attribute value. :type value: QNetworkAccessManager """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format( self.__class__.__name__, "networkAccessManager")) @networkAccessManager.deleter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def networkAccessManager(self): """ Deleter for **self.__networkAccessManager** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format( self.__class__.__name__, "networkAccessManager")) @property def releaseReply(self): """ Property for **self.__releasesFileReply** attribute. :return: self.__releasesFileReply. :rtype: QNetworkReply """ return self.__releasesFileReply @releaseReply.setter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def releaseReply(self, value): """ Setter for **self.__releasesFileReply** attribute. :param value: Attribute value. :type value: QNetworkReply """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format( self.__class__.__name__, "releaseReply")) @releaseReply.deleter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def releaseReply(self): """ Deleter for **self.__releasesFileReply** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format( self.__class__.__name__, "releaseReply")) @property def remoteUpdater(self): """ Property for **self.__remoteUpdater** attribute. :return: self.__remoteUpdater. :rtype: object """ return self.__remoteUpdater @remoteUpdater.setter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def remoteUpdater(self, value): """ Setter for **self.__remoteUpdater** attribute. :param value: Attribute value. :type value: object """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format( self.__class__.__name__, "remoteUpdater")) @remoteUpdater.deleter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def remoteUpdater(self): """ Deleter for **self.__remoteUpdater** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format( self.__class__.__name__, "remoteUpdater")) @property def reportUpdateStatus(self): """ Property for **self.__reportUpdateStatus** attribute. :return: self.__reportUpdateStatus. :rtype: bool """ return self.__reportUpdateStatus @reportUpdateStatus.setter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def reportUpdateStatus(self, value): """ Setter for **self.__reportUpdateStatus** attribute. :param value: Attribute value. :type value: bool """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is read only!".format( self.__class__.__name__, "reportUpdateStatus")) @reportUpdateStatus.deleter @foundations.exceptions.handleExceptions( foundations.exceptions.ProgrammingError) def reportUpdateStatus(self): """ Deleter for **self.__reportUpdateStatus** attribute. """ raise foundations.exceptions.ProgrammingError( "{0} | '{1}' attribute is not deletable!".format( self.__class__.__name__, "reportUpdateStatus")) #****************************************************************************************************************** #*** Class methods. #****************************************************************************************************************** def activate(self, engine): """ Activates the Component. :param engine: Engine to attach the Component to. :type engine: QObject :return: Method success. :rtype: bool """ LOGGER.debug("> Activating '{0}' Component.".format( self.__class__.__name__)) self.__engine = engine self.__settings = self.__engine.settings self.__settingsSection = self.name self.__preferencesManager = self.__engine.componentsManager[ "factory.preferencesManager"] self.__templatesOutliner = self.__engine.componentsManager[ "core.templatesOutliner"] self.__locationsBrowser = self.__engine.componentsManager[ "addons.locationsBrowser"] self.__ioDirectory = os.path.join( self.__engine.userApplicationDataDirectory, Constants.ioDirectory, self.__ioDirectory) not foundations.common.pathExists(self.__ioDirectory) and os.makedirs( self.__ioDirectory) self.__networkAccessManager = QNetworkAccessManager() self.__reportUpdateStatus = True self.activated = True return True def deactivate(self): """ Deactivates the Component. :return: Method success. :rtype: bool """ LOGGER.debug("> Deactivating '{0}' Component.".format( self.__class__.__name__)) self.__engine = None self.__settings = None self.__settingsSection = None self.__preferencesManager = None self.__templatesOutliner = None self.__locationsBrowser = None self.__ioDirectory = os.path.basename( os.path.abspath(self.__ioDirectory)) self.__networkAccessManager = None self.__reportUpdateStatus = None self.activated = False return True def initializeUi(self): """ Initializes the Component ui. :return: Method success. :rtype: bool """ LOGGER.debug("> Initializing '{0}' Component ui.".format( self.__class__.__name__)) self.__engine.parameters.deactivateWorkerThreads and \ LOGGER.info( "{0} | 'OnStartup' Online Updater worker thread deactivated by '{1}' command line parameter value!".format( self.__class__.__name__, "deactivateWorkerThreads")) self.__Check_For_New_Releases_On_Startup_checkBox_setUi() self.__Ignore_Non_Existing_Templates_checkBox_setUi() # Signals / Slots. self.Check_For_New_Releases_pushButton.clicked.connect( self.__Check_For_New_Releases_pushButton__clicked) self.Check_For_New_Releases_On_Startup_checkBox.stateChanged.connect( self.__Check_For_New_Releases_On_Startup_checkBox__stateChanged) self.Ignore_Non_Existing_Templates_checkBox.stateChanged.connect( self.__Ignore_Non_Existing_Templates_checkBox__stateChanged) self.initializedUi = True return True def uninitializeUi(self): """ Uninitializes the Component ui. :return: Method success. :rtype: bool """ LOGGER.debug("> Uninitializing '{0}' Component ui.".format( self.__class__.__name__)) # Signals / Slots. self.Check_For_New_Releases_pushButton.clicked.disconnect( self.__Check_For_New_Releases_pushButton__clicked) self.Check_For_New_Releases_On_Startup_checkBox.stateChanged.disconnect( self.__Check_For_New_Releases_On_Startup_checkBox__stateChanged) self.Ignore_Non_Existing_Templates_checkBox.stateChanged.disconnect( self.__Ignore_Non_Existing_Templates_checkBox__stateChanged) self.initializedUi = False return True def onStartup(self): """ Defines the slot triggered on Framework startup. :return: Method success. :rtype: bool """ LOGGER.debug( "> Calling '{0}' Component Framework 'onStartup' method.".format( self.__class__.__name__)) self.__reportUpdateStatus = False if not self.__engine.parameters.deactivateWorkerThreads and \ self.Check_For_New_Releases_On_Startup_checkBox.isChecked(): self.checkForNewReleases() return True def addWidget(self): """ Adds the Component Widget to the engine. :return: Method success. :rtype: bool """ LOGGER.debug("> Adding '{0}' Component Widget.".format( self.__class__.__name__)) self.__preferencesManager.Others_Preferences_gridLayout.addWidget( self.Online_Updater_groupBox) def removeWidget(self): """ Removes the Component Widget from the engine. :return: Method success. :rtype: bool """ LOGGER.debug("> Removing '{0}' Component Widget.".format( self.__class__.__name__)) self.Online_Updater_groupBox.setParent(None) def __Check_For_New_Releases_On_Startup_checkBox_setUi(self): """ Sets the **Check_For_New_Releases_On_Startup_checkBox** Widget. """ # Adding settings key if it doesn't exists. self.__settings.getKey(self.__settingsSection, "checkForNewReleasesOnStartup").isNull() and \ self.__settings.setKey(self.__settingsSection, "checkForNewReleasesOnStartup", Qt.Checked) checkForNewReleasesOnStartup = self.__settings.getKey( self.__settingsSection, "checkForNewReleasesOnStartup") LOGGER.debug("> Setting '{0}' with value '{1}'.".format( "Check_For_New_Releases_On_Startup_checkBox", foundations.common.getFirstItem( checkForNewReleasesOnStartup.toInt()))) self.Check_For_New_Releases_On_Startup_checkBox.setCheckState( foundations.common.getFirstItem( checkForNewReleasesOnStartup.toInt())) def __Check_For_New_Releases_On_Startup_checkBox__stateChanged( self, state): """ Defines the slot triggered by **Check_For_New_Releases_On_Startup_checkBox** Widget when state changed. :param state: Checkbox state. :type state: int """ LOGGER.debug( "> Check for new releases on startup state: '{0}'.".format(state)) self.__settings.setKey(self.__settingsSection, "checkForNewReleasesOnStartup", state) def __Ignore_Non_Existing_Templates_checkBox_setUi(self): """ Sets the **Ignore_Non_Existing_Templates_checkBox** Widget. """ # Adding settings key if it doesn't exists. self.__settings.getKey(self.__settingsSection, "ignoreNonExistingTemplates").isNull() and \ self.__settings.setKey(self.__settingsSection, "ignoreNonExistingTemplates", Qt.Checked) ignoreNonExistingTemplates = self.__settings.getKey( self.__settingsSection, "ignoreNonExistingTemplates") LOGGER.debug("> Setting '{0}' with value '{1}'.".format( "Ignore_Non_Existing_Templates_checkBox", foundations.common.getFirstItem( ignoreNonExistingTemplates.toInt()))) self.Ignore_Non_Existing_Templates_checkBox.setCheckState( foundations.common.getFirstItem( ignoreNonExistingTemplates.toInt())) def __Ignore_Non_Existing_Templates_checkBox__stateChanged(self, state): """ Defines the slot triggered by **Ignore_Non_Existing_Templates_checkBox** Widget when state changed. :param state: Checkbox state. :type state: int """ LOGGER.debug( "> Ignore non existing Templates state: '{0}'.".format(state)) self.__settings.setKey(self.__settingsSection, "ignoreNonExistingTemplates", state) def __Check_For_New_Releases_pushButton__clicked(self, checked): """ Defines the slot triggered by **Check_For_New_Releases_pushButton** Widget when clicked. :param checked: Checked state. :type checked: bool """ self.checkForNewReleasesUi() @foundations.exceptions.handleExceptions(sibl_gui.exceptions.NetworkError) def __releasesFileReply__finished(self): """ Defines the slot triggered by the releases file reply when finished. """ self.__engine.stopProcessing() if not self.__releasesFileReply.error(): content = [] while not self.__releasesFileReply.atEnd(): content.append( foundations.strings.toString( self.__releasesFileReply.readLine())) LOGGER.debug("> Parsing releases file content.") sectionsFileParser = SectionsFileParser() sectionsFileParser.content = content sectionsFileParser.parse() releases = {} for remoteObject in sectionsFileParser.sections: if remoteObject != Constants.applicationName: databaseTemplates = \ sibl_gui.components.core.database.operations.filterTemplates("^{0}$".format(remoteObject), "name") databaseTemplate = foundations.common.getFirstItem([ foundations.common.getFirstItem(databaseTemplate) for databaseTemplate in sorted(((databaseTemplate, databaseTemplate.release) for databaseTemplate in databaseTemplates), reverse=True, key=lambda x: (foundations.strings.getVersionRank(x[1]))) ]) if not self.__engine.parameters.databaseReadOnly: if databaseTemplate: if databaseTemplate.release != sectionsFileParser.getValue( "Release", remoteObject): releases[remoteObject] = ReleaseObject( name=remoteObject, repositoryVersion=sectionsFileParser. getValue("Release", remoteObject), localVersion=databaseTemplate.release, type=sectionsFileParser.getValue( "Type", remoteObject), url=sectionsFileParser.getValue( "Url", remoteObject), comment=sectionsFileParser.getValue( "Comment", remoteObject)) else: if not self.Ignore_Non_Existing_Templates_checkBox.isChecked( ): releases[remoteObject] = ReleaseObject( name=remoteObject, repositoryVersion=sectionsFileParser. getValue("Release", remoteObject), localVersion=None, type=sectionsFileParser.getValue( "Type", remoteObject), url=sectionsFileParser.getValue( "Url", remoteObject), comment=sectionsFileParser.getValue( "Comment", remoteObject)) else: LOGGER.info( "{0} | '{1}' repository remote object skipped by '{2}' command line parameter value!" .format(self.__class__.__name__, remoteObject, "databaseReadOnly")) else: if Constants.version != sectionsFileParser.getValue( "Release", remoteObject): releases[remoteObject] = ReleaseObject( name=remoteObject, repositoryVersion=sectionsFileParser.getValue( "Release", remoteObject), localVersion=Constants.version, url=sectionsFileParser.getValue( "Url", remoteObject), type=sectionsFileParser.getValue( "Type", remoteObject), comment=None) if releases: LOGGER.debug("> Initializing Remote Updater.") self.__remoteUpdater = RemoteUpdater(self, releases, Qt.Window) self.__remoteUpdater.show() else: self.__reportUpdateStatus and self.__engine.notificationsManager.notify( "{0} | '{1}' is up to date!".format( self.__class__.__name__, Constants.applicationName)) else: raise sibl_gui.exceptions.NetworkError( "{0} | QNetworkAccessManager error code: '{1}'.".format( self.__class__.__name__, self.__releasesFileReply.error())) def __getReleasesFile(self, url): """ Gets the releases file. """ LOGGER.debug("> Downloading '{0}' releases file.".format(url.path())) self.__engine.startProcessing("Retrieving Releases File ...") self.__releasesFileReply = self.__networkAccessManager.get( QNetworkRequest(url)) self.__releasesFileReply.finished.connect( self.__releasesFileReply__finished) @foundations.exceptions.handleExceptions( umbra.exceptions.notifyExceptionHandler, sibl_gui.exceptions.NetworkError, Exception) def checkForNewReleasesUi(self): """ Checks for new releases. :return: Method success. :rtype: bool :note: May require user interaction. """ if not self.__networkAccessManager.networkAccessible(): raise sibl_gui.exceptions.NetworkError( "{0} | Network is not accessible!".format( self.__class__.__name__)) self.__reportUpdateStatus = True if self.checkForNewReleases(): return True else: raise Exception( "{0} | Exception raised while checking for new releases!". format(self.__class__.__name__)) @foundations.exceptions.handleExceptions(sibl_gui.exceptions.NetworkError, Exception) def checkForNewReleases(self): """ Checks for new releases. :return: Method success. :rtype: bool """ if not self.__networkAccessManager.networkAccessible(): raise sibl_gui.exceptions.NetworkError( "{0} | Network is not accessible!".format( self.__class__.__name__)) self.__getReleasesFile( QUrl(os.path.join(self.__repositoryUrl, self.__releasesFileUrl))) return True