Beispiel #1
0
    def saveCtrl(self):
        n = 0
        if self.lineEdit_3.text()=='':
            n += 1
        if self.lineEdit_4.text()=='':
            n += 1
        if self.lineEdit_5.text()=='':
            n += 1
        if self.comboBox_2.currentText()=='':
            n += 1
        if self.comboBox_3.currentText()=='':
            n += 1
        if self.comboBox_4.currentText()=='':
            n += 1
        if self.tableWidget.rowCount()==0:
            n += 1

        if n==0:
            self.btnsave.setEnabled(True)
            self.label_12.setText('')
        else:
            self.btnsave.setEnabled(False)
            pal = QPalette()
            pal.setColor(self.label_12.backgroundRole(), Qt.red)
            pal.setColor(self.label_12.foregroundRole(), Qt.red)
            self.label_12.setPalette(pal)
            self.label_12.setText('All bold field must be given!')
Beispiel #2
0
    def saveCtrl(self):
        n = 0
        if self.lineEdit_3_id.text() == '':
            n += 1
        if self.lineEdit_4_year.text() == '':
            n += 1
        if self.lineEdit_5_code.text() == '':
            n += 1
        if self.comboBox_2_disease.currentText() == '':
            n += 1
        if self.comboBox_3_status.currentText() == '':
            n += 1
        if self.comboBox_4_large_scale.currentText() == '':
            n += 1
        if self.tableWidget.rowCount() == 0:
            n += 1

        if n == 0:
            self.btnsave.setEnabled(True)
            self.label_12.setText('')
        else:
            self.btnsave.setEnabled(False)
            pal = QPalette()
            pal.setColor(self.label_12.backgroundRole(), Qt.red)
            pal.setColor(self.label_12.foregroundRole(), Qt.red)
            self.label_12.setPalette(pal)
            self.label_12.setText('All bold field must be given!')
Beispiel #3
0
    def db_size(self, db_connections):
        usedSpace = 0
        self.numDbs = len(list(db_connections._dbs.keys()))
        for db in list(db_connections._dbs.keys()):
            try:
                conn = db_connections.db(db).psycopg_connection()
            except:
                continue
            cursor = conn.cursor()
            sql = "SELECT pg_database_size('" + str(db) + "')"
            cursor.execute(sql)
            usedSpace += int(cursor.fetchone()[0]) - (11 * 1024 * 1024)
            cursor.close()
            conn.close

        # Used space in MB
        usedSpace /= 1024 * 1024

        login_info = self.api.check_login(version_info=self._version_info())

        try:
            self.maxSize = login_info['max_storage']
            self.maxDBs = login_info['max_dbs']
        except:
            self.maxSize = 50
            self.maxDBs = 5

        lblPalette = QPalette(self.ui.lblDbSize.palette())
        usage = usedSpace / float(self.maxSize)
        self.storage_exceeded = False

        if usage < 0.8:
            bg_color = QColor(255, 0, 0, 0)
            text_color = QColor(Qt.black)
        elif usage >= 0.8 and usage < 1:
            bg_color = QColor(255, 0, 0, 100)
            text_color = QColor(Qt.white)
        elif usage >= 1:
            bg_color = QColor(255, 0, 0, 255)
            text_color = QColor(Qt.white)
            self.storage_exceeded = True

        lblPalette.setColor(QPalette.Window, QColor(bg_color))
        lblPalette.setColor(QPalette.Foreground, QColor(text_color))

        self.ui.lblDbSize.setAutoFillBackground(True)
        self.ui.lblDbSize.setPalette(lblPalette)
        self.ui.lblDbSize.setText(
            self.tr("Used DB Storage: ") + "%d / %d MB" %
            (usedSpace, self.maxSize))

        self.ui.lblDbSizeUpload.setAutoFillBackground(True)
        self.ui.lblDbSizeUpload.setPalette(lblPalette)
        self.ui.lblDbSizeUpload.setText(
            self.tr("Used DB Storage: ") + "%d / %d MB" %
            (usedSpace, self.maxSize))
        self.ui.btnUploadData.setDisabled(self.storage_exceeded)
        self.ui.btnPublishMap.setDisabled(self.storage_exceeded)
    def db_size(self,  db_connections):
        usedSpace = 0
        self.numDbs = len(list(db_connections._dbs.keys()))
        for db in list(db_connections._dbs.keys()):
            try:
                conn = db_connections.db(db).psycopg_connection()
            except:
                continue
            cursor = conn.cursor()
            sql = "SELECT pg_database_size('" + str(db) + "')"
            cursor.execute(sql)
            usedSpace += int(cursor.fetchone()[0])-(11*1024*1024)
            cursor.close()
            conn.close

        # Used space in MB
        usedSpace /= 1024 * 1024

        login_info = self.api.check_login(version_info=self._version_info())
        
        try:            
            self.maxSize = login_info['max_storage']
            self.maxDBs = login_info['max_dbs']
        except:
            self.maxSize = 50
            self.maxDBs = 5

        lblPalette = QPalette(self.ui.lblDbSize.palette())
        usage = usedSpace / float(self.maxSize)
        self.storage_exceeded = False
    
        if usage < 0.8:
            bg_color = QColor(255, 0, 0, 0)
            text_color = QColor(Qt.black)
        elif usage >= 0.8 and usage < 1:
            bg_color = QColor(255, 0, 0, 100)
            text_color = QColor(Qt.white)
        elif usage >= 1:
            bg_color = QColor(255, 0, 0, 255)
            text_color = QColor(Qt.white)
            self.storage_exceeded = True

        lblPalette.setColor(QPalette.Window, QColor(bg_color))
        lblPalette.setColor(QPalette.Foreground,QColor(text_color))
        
        self.ui.lblDbSize.setAutoFillBackground(True)
        self.ui.lblDbSize.setPalette(lblPalette)
        self.ui.lblDbSize.setText(
            self.tr("Used DB Storage: ") + "%d / %d MB" % (usedSpace, self.maxSize))

        self.ui.lblDbSizeUpload.setAutoFillBackground(True)
        self.ui.lblDbSizeUpload.setPalette(lblPalette)
        self.ui.lblDbSizeUpload.setText(
            self.tr("Used DB Storage: ") + "%d / %d MB" % (usedSpace, self.maxSize))
        self.ui.btnUploadData.setDisabled(self.storage_exceeded)
        self.ui.btnPublishMap.setDisabled(self.storage_exceeded)
Beispiel #5
0
 def update_title(self):
     if self.operationName.text().strip() == '':
         palette = QPalette()
         palette.setColor(QPalette.Text, Qt.red)
         self.operation_gbox.setPalette(palette)
         self.operation_gbox.setTitle(u"O Nome da Operação não está definido.")
     else:
         palette = QPalette()
         palette.setColor(QPalette.Text, Qt.black)
         self.operation_gbox.setPalette(palette)
         self.operation_gbox.setTitle(self.operationName.text())
     self.parent.check_operations_completness()
Beispiel #6
0
    def nameCtrl(self):
        ss = 'zone_a_%s' % self.lineEdit.text()
        if ss in self.tablst:
            self.btnok.setEnabled(False)
            pal = QPalette()
            pal.setColor(self.label_17.backgroundRole(), Qt.red)
            pal.setColor(self.label_17.foregroundRole(), Qt.red)
            self.label_17.setPalette(pal)
            self.label_17.setText('Database contain layer with tis name!')
        else:
            self.btnok.setEnabled(True)

        self.saveCtrl()
    def vfkFileLineEdit_textChanged(self, arg1):
        """

        :type arg1: str
        :return:
        """
        info = QFileInfo(arg1)

        if info.isFile():
            self.loadVfkButton.setEnabled(True)
            self.vfkFileLineEdit.setPalette(self.__mDefaultPalette)
        else:
            self.loadVfkButton.setEnabled(False)
            pal = QPalette(self.vfkFileLineEdit.palette())
            pal.setColor(QPalette.text(), Qt.red)
            self.vfkFileLineEdit.setPalette(pal)
Beispiel #8
0
    def saveCtrl(self):
        n = 0
        if self.lineEdit.text()=='':
            n += 1
        if self.comboBox_3.currentText()=='':
            n += 1
        if self.tableWidget.rowCount()==0:
            n += 1

        if n==0:
            self.btnok.setEnabled(True)
            self.label_17.setText('')
        else:
            self.btnok.setEnabled(False)
            pal = QPalette()
            pal.setColor(self.label_17.backgroundRole(), Qt.red)
            pal.setColor(self.label_17.foregroundRole(), Qt.red)
            self.label_17.setPalette(pal)
            self.label_17.setText('All bold field must be given!')
    def updateTitle(self):

        texto = self.email.model().data(self.email.model().index(0, 0))
        if texto is None:
            texto = self.name.text().strip()

        texto += u" \u21D2 " + self.organization.text() + "-" + self.city.text(
        ) + "-" + self.country.text()
        texto = re.sub(r"^\-*", "", texto.strip())
        texto = re.sub(r"\-*$", "", texto.strip())
        texto = re.sub(r'\-\-+', '-', texto.strip())
        if texto.strip() == u'\u21D2':
            palette = QPalette()
            palette.setColor(QPalette.Text, Qt.red)
            self.mGroupBox.setPalette(palette)
            self.mGroupBox.setTitle(u"Contacto Incompleto \u26a0")
            self.setToolTip(u"Contacto Incompleto.")
        elif self.email.model().rowCount() == 0 or self.organization.text(
        ) == "":
            palette = QPalette()
            palette.setColor(QPalette.Text, Qt.red)
            self.mGroupBox.setPalette(palette)
            self.mGroupBox.setTitle(texto)
            self.setToolTip(u"Contacto Incompleto.")
        else:
            palette = QPalette()
            palette.setColor(QPalette.Text, Qt.black)
            self.mGroupBox.setPalette(palette)
            self.mGroupBox.setTitle(texto)
            self.setToolTip(u"")
        self.check_mandatory_completude()
Beispiel #10
0
    def validate_date_string(self):  # FIXME check for double input e.g. %H%H
        list_widget = self.parent.extractionPriorityListWidget
        valid = True

        pattern_line = self.pattern_edit
        pal = QPalette(pattern_line.palette())

        pattern = self.parent.patternLineEdit.text()
        sample = self.parent.sampleLineEdit.text()
        today_string = ''
        i = 0
        from_file_name_checked = False
        # orig_color = pal.background()

        while i < list_widget.count():

            if list_widget.item(i).text() == "Filename" and \
                    list_widget.item(i).checkState() == Qt.Checked:
                from_file_name_checked = True
            i += 1
        if not from_file_name_checked:
            pal.setColor(QPalette.Base, QColor('white'))
            pattern_line.setPalette(pal)
            return
        try:
            datetime.datetime.strptime(sample, pattern)
        except ValueError:
            valid = False
        try:
            today_string = datetime.datetime.strftime(
                datetime.datetime.today(), pattern)
        except ValueError:
            valid = False
        if len(today_string) != len(sample):
            valid = False
        if valid:
            pal.setColor(QPalette.Base, QColor(0, 255, 0, 127))  # light green
        else:
            pal.setColor(QPalette.Base, QColor(255, 0, 0, 127))  # light red
        pattern_line.setPalette(pal)
class QgisCloudPluginDialog(QDockWidget):
    COLUMN_LAYERS = 0
    COLUMN_DATA_SOURCE = 1
    COLUMN_TABLE_NAME = 2
    COLUMN_GEOMETRY_TYPE = 3
    COLUMN_SRID = 4

    GEOMETRY_TYPES = {
        QgsWkbTypes.Unknown: "Unknown",
        QgsWkbTypes.NoGeometry: "No geometry",
        QgsWkbTypes.Point: "Point",
        QgsWkbTypes.MultiPoint: "MultiPoint",               
        QgsWkbTypes.PointZ: "PointZ",
        QgsWkbTypes.MultiPointZ: "MultiPointZ",
        QgsWkbTypes.PointM: "PointM",
        QgsWkbTypes.MultiPointM: "MultiPointM",
        QgsWkbTypes.PointZM: "PointZM",
        QgsWkbTypes.MultiPointZM: "MultiPointZM",
        QgsWkbTypes.Point25D: "Point25D",
        QgsWkbTypes.MultiPoint25D: "MultiPoint25D",
        QgsWkbTypes.LineString: "LineString",
        QgsWkbTypes.MultiLineString: "LineString",
        QgsWkbTypes.LineStringZ: "LineStringZ",
        QgsWkbTypes.MultiLineStringZ: "LineStringZ",
        QgsWkbTypes.LineStringM: "LineStringM",
        QgsWkbTypes.MultiLineStringM: "LineStringM",
        QgsWkbTypes.LineStringZM: "LineStringZM",
        QgsWkbTypes.MultiLineStringZM: "LineStringZM",
        QgsWkbTypes.LineString25D: "LineString25D",
        QgsWkbTypes.MultiLineString25D: "MultiLineString25D",
        QgsWkbTypes.Polygon: "Polygon",            
        QgsWkbTypes.MultiPolygon: "MultiPolygon", 
        QgsWkbTypes.PolygonZ: "PolygonZ",            
        QgsWkbTypes.MultiPolygonZ: "MultiPolygonZ", 
        QgsWkbTypes.PolygonM: "PolygonM",            
        QgsWkbTypes.MultiPolygonM: "MultiPolygonM", 
        QgsWkbTypes.PolygonZM: "PolygonZM",            
        QgsWkbTypes.MultiPolygonZM: "MultiPolygonZM", 
        QgsWkbTypes.Polygon25D: "Polygon25D",            
        QgsWkbTypes.MultiPolygon25D: "MultiPolygon25D", 
        QgsWkbTypes.CircularString: "CircularString",
        QgsWkbTypes.CompoundCurve: "CompoundCurve",
        QgsWkbTypes.CurvePolygon: "CurvePolygon", 
        QgsWkbTypes.MultiCurve: "MultiCurve",
        QgsWkbTypes.MultiSurface: "MultiSurface",
        QgsWkbTypes.CircularStringZ: "CircularStringZ",
        QgsWkbTypes.CompoundCurveZ: "CompoundCurveZ",
        QgsWkbTypes.CurvePolygonZ: "CurvePolygonZ", 
        QgsWkbTypes.MultiCurveZ: "MultiCurveZ",
        QgsWkbTypes.MultiSurfaceZ: "MultiSurfaceZ",
        QgsWkbTypes.CircularStringM: "CircularStringM",
        QgsWkbTypes.CompoundCurveM: "CompoundCurveM",
        QgsWkbTypes.CurvePolygonM: "CurvePolygonM", 
        QgsWkbTypes.MultiCurveM: "MultiCurveM",
        QgsWkbTypes.MultiSurfaceM: "MultiSurfaceM",
        QgsWkbTypes.CircularStringZM: "CircularStringZM",
        QgsWkbTypes.CompoundCurveZM: "CompoundCurveZM",
        QgsWkbTypes.CurvePolygonZM: "CurvePolygonZM", 
        QgsWkbTypes.MultiCurveZM: "MultiCurveZM",
        QgsWkbTypes.MultiSurfaceZM: "MultiSurfaceZM",
    }
        
    PROJECT_INSTANCE = QgsProject.instance()     
    
    
    def __init__(self, iface, version):
        QDockWidget.__init__(self, None)
        self.iface = iface
        self.clouddb = True
        self.version = version
        # Set up the user interface from Designer.
        self.ui = Ui_QgisCloudPlugin()
        self.ui.setupUi(self)
        self.storage_exceeded = True
        
        myAbout = DlgAbout()
        self.ui.aboutText.setText(
            myAbout.aboutString() +
            myAbout.contribString() +
            myAbout.licenseString() +
            "<p>Versions:<ul>" +
            "<li>QGIS: %s</li>" % str(Qgis.QGIS_VERSION).encode("utf-8") +
            "<li>Python: %s</li>" % sys.version.replace("\n", " ") +
            "<li>OS: %s</li>" % platform.platform() +
            "</ul></p>")
            
        data_protection_link = """<a href="http://qgiscloud.com/pages/privacy">%s</a>""" % (self.tr("Privacy Policy"))
            
        self.ui.lblVersionPlugin.setText("%s &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;%s" % (self.version,  data_protection_link))
        self.ui.lblVersionPlugin.setOpenExternalLinks(True)            
#        self.ui.lblVersionPlugin.setText(self.version)

        self.ui.tblLocalLayers.setColumnCount(5)
        header = ["Layers", "Data source",
                  "Table name", "Geometry type", "SRID"]
        self.ui.tblLocalLayers.setHorizontalHeaderLabels(header)
        self.ui.tblLocalLayers.resizeColumnsToContents()
        self.ui.tblLocalLayers.setEditTriggers(QAbstractItemView.NoEditTriggers)

        self.ui.btnUploadData.setEnabled(False)
        self.ui.btnPublishMap.setEnabled(False)
        self.ui.btnMapDelete.setEnabled(False)
        self.ui.progressWidget.hide()
        self.ui.btnLogout.hide()
        self.ui.lblLoginStatus.hide()
        self.ui.widgetServices.hide()
        self.ui.widgetDatabases.setEnabled(False)
        self.ui.widgetMaps.setEnabled(False)
        self.ui.labelOpenLayersPlugin.hide()

        try:
            from .openlayers_menu import OpenlayersMenu
            self.ui.btnBackgroundLayer.setMenu(OpenlayersMenu(self.iface))
        except:
                self.ui.btnBackgroundLayer.hide()
                self.ui.labelOpenLayersPlugin.show()

        # map<data source, table name>
        self.data_sources_table_names = {}
        # flag to disable update of local data sources during upload
        self.do_update_local_data_sources = True

        self.ui.btnLogin.clicked.connect(self.check_login)
        self.ui.btnDbCreate.clicked.connect(self.create_database)
        self.ui.btnDbDelete.clicked.connect(self.delete_database)
        self.ui.btnDbRefresh.clicked.connect(self.refresh_databases)
        self.ui.btnMapDelete.clicked.connect(self.delete_map)
        self.ui.btnMapLoad.clicked.connect(self.map_load)
        self.ui.tabMaps.itemDoubleClicked.connect(self.map_load)
        self.ui.tabDatabases.itemSelectionChanged.connect(self.select_database)
        self.ui.tabMaps.itemSelectionChanged.connect(self.select_map)
        self.ui.btnPublishMap.clicked.connect(self.publish_map)
        self.ui.btnRefreshLocalLayers.clicked.connect(self.refresh_local_data_sources)
        self.iface.newProjectCreated.connect(self.reset_load_data)
        self.iface.projectRead.connect(self.reset_load_data)

        self.PROJECT_INSTANCE.layerWillBeRemoved.connect(self.remove_layer)
        self.PROJECT_INSTANCE.layerWasAdded.connect(self.add_layer)
        
        self.ui.cbUploadDatabase.currentIndexChanged.connect(lambda idx: self.activate_upload_button())
        self.ui.btnUploadData.clicked.connect(self.upload_data)

        self.ui.editServer.textChanged.connect(self.serverURL)
        self.ui.resetUrlBtn.clicked.connect(self.resetApiUrl)

        self.read_settings()
        self.api = API()
        self.db_connections = DbConnections()
        self.local_data_sources = LocalDataSources()
        self.data_upload = DataUpload(
            self.iface, self.statusBar(), self.ui.lblProgress, self.api,
            self.db_connections)

        if self.URL == "":
            self.ui.editServer.setText(self.api.api_url())
        else:
            self.ui.editServer.setText(self.URL)

        self.palette_red = QPalette(self.ui.lblVersionPlugin.palette())
        self.palette_red.setColor(QPalette.WindowText, Qt.red)

    def unload(self):
        self.do_update_local_data_sources = False
        if self.iface:
            self.iface.newProjectCreated.disconnect(self.reset_load_data)
            self.iface.projectRead.disconnect(self.reset_load_data)
        
        if self.PROJECT_INSTANCE:
            self.PROJECT_INSTANCE.layerWillBeRemoved.disconnect(self.remove_layer)
            self.PROJECT_INSTANCE.layerWasAdded.disconnect(self.add_layer)
        

    def statusBar(self):
        return self.iface.mainWindow().statusBar()

    def map(self):
        project = QgsProject.instance()
        name = os.path.splitext(os.path.basename(str(project.fileName())))[0]
            
        # Allowed chars for QGISCloud map name: /\A[A-Za-z0-9\_\-]*\Z/
        name = str(name).encode('ascii', 'replace')  # Replace non-ascii chars
        
        # Replace withespace
        name = name.replace(b" ",  b"_")
        return name

    def resetApiUrl(self):
        self.ui.editServer.setText(self.api.api_url())

    def serverURL(self, URL):
        self.URL = URL
        self.store_settings()

    def store_settings(self):
        s = QSettings()
        s.setValue("qgiscloud/user", self.user)
        s.setValue("qgiscloud/URL", self.URL)

    def read_settings(self):
        s = QSettings()
        self.user = s.value("qgiscloud/user", "")
        self.URL = s.value("qgiscloud/URL", "")

    def _update_clouddb_mode(self, clouddb):
        self.clouddb = clouddb
        self.ui.widgetDatabases.setVisible(self.clouddb)
        tab_index = 1
        tab_name = QApplication.translate("QgisCloudPlugin", "Upload Data")
        visible = (self.ui.tabWidget.indexOf(self.ui.uploadTab) == tab_index)
        if visible and not self.clouddb:
            self.ui.tabWidget.removeTab(tab_index)
        elif not visible and self.clouddb:
            self.ui.tabWidget.insertTab(tab_index, self.ui.uploadTab, tab_name)

    def _version_info(self):
        return {
            'versions': {
                'plugin': self.version.encode('utf-8').decode('utf-8'),
                'QGIS': Qgis.QGIS_VERSION.encode('utf-8').decode('utf-8'),
                'OS': platform.platform().encode('utf-8').decode('utf-8'),
                'Python': sys.version.encode('utf-8').decode('utf-8')
            }
        }

    def check_login(self):
        version_ok = True
        if not self.api.check_auth():
#            res = QMessageBox.information(
#                self,
#                self.tr("QGIS 3 message"),
#                self.tr("""You have started the QGIS Cloud Plugin with QGIS 3. This configuration is not stable and is not intended for productive use. A lot can still change in QGIS before the first QGIS 3 LTR. Therefore, it is possible that the plugin does not work as you expect or terminates with errors. If you need a stable version of QGIS Cloud, please continue working with QGIS 2."""),
#                QMessageBox.StandardButtons(
#                QMessageBox.Close))        	
            login_dialog = QDialog(self)
            login_dialog.ui = Ui_LoginDialog()
            login_dialog.ui.setupUi(login_dialog)
            login_dialog.ui.editUser.setText(self.user)
            login_ok = False
            while not login_ok and version_ok:
                self.api.set_url(self.api_url())
                if not login_dialog.exec_():
                    self.api.set_auth(
                        user=login_dialog.ui.editUser.text(), password=None)
                    return login_ok
                self.api.set_auth(
                    user=login_dialog.ui.editUser.text(),
                    password=login_dialog.ui.editPassword.text())
                try:
                    login_info = self.api.check_login(
                        version_info=self._version_info())

# QGIS private Cloud has no tos_accepted
                    try:
                        if not login_info['tos_accepted']:
                            result = QMessageBox.information(
                                None,
                                self.tr("Accept new Privacy Policy"),
                                self.tr("""Due to the GDPR qgiscloud.com has a new <a href='http://qgiscloud.com/en/pages/privacy'> Privacy Policy </a>. 
                                To continue using qgiscloud.com, you must accept the new policy. """),
                                QMessageBox.StandardButtons(
                                    QMessageBox.No |
                                    QMessageBox.Yes))
                            
                            if result == QMessageBox.No:
                                login_ok = False
                                return
                            else:
                                result = self.api.accept_tos()
                    except:
                        pass
                            
                    self.user = login_dialog.ui.editUser.text()
                    self._update_clouddb_mode(login_info['clouddb'])
                    version_ok = StrictVersion(self.version) >= StrictVersion(login_info['current_plugin'])
                    if not version_ok:
                        self.ui.lblVersionPlugin.setPalette(self.palette_red)
                        QMessageBox.information(None, self.tr('New Version'),  self.tr('New plugin release {version} is available! Please upgrade the QGIS Cloud plugin.').format(version=login_info['current_plugin']))
                    self.store_settings()
                    self.ui.btnLogin.hide()
                    self.ui.lblSignup.hide()
                    self.ui.btnLogout.show()
                    self.ui.widgetDatabases.setEnabled(True)
                    self.ui.widgetMaps.setEnabled(True)

                    self.ui.lblLoginStatus.setText(
                        self.tr("Logged in as {0} ({1})").format(self.user, login_info['plan']))
                    self.ui.lblLoginStatus.show()
                    self._push_message(
                        self.tr("QGIS Cloud"),
                        self.tr("Logged in as {0}").format(self.user),
                        level=0, duration=2)
                    self.refresh_databases()
                    self.refresh_maps()
                    if not version_ok:
                        self._push_message(self.tr("QGIS Cloud"), self.tr(
                            "Unsupported versions detected. Please check your versions first!"), level=1)
                        version_ok = False
                        self.ui.tabWidget.setCurrentWidget(self.ui.aboutTab)
                    login_ok = True
                    self.update_local_layers()
            
                except ForbiddenError:
                    QMessageBox.critical(
                        self, self.tr("Account Disabled"),
                        self.tr("Account {username} is disabled! Please contact [email protected]").format(username=login_dialog.ui.editUser.text()))
                    login_ok = False                    
                except UnauthorizedError:
                    QMessageBox.critical(
                        self, self.tr("Login for user {username} failed").format(username=login_dialog.ui.editUser.text()),
                        self.tr("Wrong user name or password"))
                    login_ok = False
                except (TokenRequiredError, ConnectionException) as e:
                    QMessageBox.critical(
                        self, self.tr("Login failed"),
                        self.tr("Login failed: %s") % str(e))
                    login_ok = False
        return version_ok

    def create_database(self):
        if self.numDbs < self.maxDBs:
            db = self.api.create_database()
            self.show_api_error(db)
            self.refresh_databases()
        else:
            QMessageBox.warning(None, self.tr('Warning!'),  self.tr('Number of %s permitted databases exceeded! Please upgrade your account!') % self.maxDBs)

    def delete_database(self):
        name = self.ui.tabDatabases.currentItem().text()
        
        answer = False
        
        for layer in list(self.PROJECT_INSTANCE.mapLayers().values()):            
            if QgsDataSourceUri(layer.publicSource()).database() == name:
                
                if not answer:
                    answer = QMessageBox.question(
                        self,
                        self.tr("Warning"),
                        self.tr('You have layers from database "{name}" loaded in your project! Do you want to remove them before you delete database "{name}"?').format(name=name),
                        QMessageBox.StandardButtons(
                            QMessageBox.Cancel |
                            QMessageBox.Yes))

                if answer == QMessageBox.Yes:
                     self.PROJECT_INSTANCE.removeMapLayer(layer.id())
                     
        if answer == QMessageBox.Cancel:
            QMessageBox.warning(None,  self.tr('Warning'),  self.tr('Deletion of database "{name}" interrupted!').format(name=name))
            return
            
        msgBox = QMessageBox()
        msgBox.setWindowTitle(self.tr("Delete QGIS Cloud database."))
        msgBox.setText(
            self.tr("Do you want to delete the database \"%s\"?") % name)
        msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
        msgBox.setDefaultButton(QMessageBox.Cancel)
        msgBox.setIcon(QMessageBox.Question)
        ret = msgBox.exec_()
        
        if ret == QMessageBox.Ok:
            self.setCursor(Qt.WaitCursor)
            result = self.api.delete_database(name)
            self.show_api_error(result)
            self.ui.btnDbDelete.setEnabled(False)
            time.sleep(2)
            self.refresh_databases()
            self.unsetCursor()

    def select_database(self):
        self.ui.btnDbDelete.setEnabled(len(self.ui.tabDatabases.selectedItems()) > 0)
            
    def select_map(self):
        self.ui.btnMapDelete.setEnabled(len(self.ui.tabMaps.selectedItems()) > 0)
        self.ui.btnMapLoad.setEnabled(len(self.ui.tabMaps.selectedItems()) > 0)
        self.update_urls(map=self.ui.tabMaps.currentItem().text())

    @pyqtSlot()
    def on_btnLogout_clicked(self):
        self.api.reset_auth()
        self.ui.btnLogout.hide()
        self.ui.lblLoginStatus.hide()
        self.ui.btnLogin.show()
        self.ui.widgetServices.hide()
        self.ui.tabDatabases.clear()
        self.ui.tabMaps.clear()
        self.ui.lblDbSize.setText("")
        self.ui.lblDbSizeUpload.setText("")
        self.ui.cbUploadDatabase.clear()
        self.ui.widgetDatabases.setEnabled(False)
        self.activate_upload_button()

    def refresh_databases(self):
        QApplication.setOverrideCursor(Qt.WaitCursor)
        if self.clouddb:
            db_list = self.api.read_databases()
            if self.show_api_error(db_list):
                QApplication.restoreOverrideCursor()
                return
            self.db_connections = DbConnections()
            for db in db_list:
                self.db_connections.add_from_json(db)

            self.ui.tabDatabases.clear()
            self.ui.btnDbDelete.setEnabled(False)
            self.ui.cbUploadDatabase.clear()
            self.ui.cbUploadDatabase.setEditable(True)
            self.ui.cbUploadDatabase.lineEdit().setReadOnly(True)
            if self.db_connections.count() == 0:
                self.ui.cbUploadDatabase.setEditText(self.tr("No databases"))
            else:
                for name, db in list(self.db_connections.iteritems()):
                    it = QListWidgetItem(name)
                    it.setToolTip(db.description())
                    self.ui.tabDatabases.addItem(it)
                    self.ui.cbUploadDatabase.addItem(name)
                if self.ui.cbUploadDatabase.count() > 1:
                    # Display the "Select database" text if more than one db is available
                    self.ui.cbUploadDatabase.setCurrentIndex(-1)
                    self.ui.cbUploadDatabase.setEditText(self.tr("Select database"))
            self.db_connections.refresh(self.user)

        self.db_size(self.db_connections)
        QApplication.restoreOverrideCursor()
        
    def map_load(self,  item=None,  row=None):     
        self.ui.widgetServices.close()
        self.setCursor(Qt.WaitCursor)
        map_id = self.ui.tabMaps.currentItem().data(Qt.UserRole)
        map_name = self.ui.tabMaps.currentItem().text()
        result = self.api.load_map_project(map_name,  map_id)
        qgs_file_name = '%s/%s.qgs' % (tempfile.gettempdir(), map_name)
        qgs_file = open(qgs_file_name,  'w')
        qgs_file.write(result)
        qgs_file.close()
        project = QgsProject.instance()
        if project.isDirty():
            ok = QMessageBox.information(
                self,
                self.tr("Save Project"),
                self.tr("""Your actual project has changes. Do you want to save the project?"""),
                QMessageBox.StandardButtons(
                    QMessageBox.Abort |
                    QMessageBox.Discard |
                    QMessageBox.Save))
            if ok:
                project.write()           
                
        project.read(qgs_file_name)
        project.setDirty(False)
        self.iface.mainWindow().setWindowTitle("QGIS %s - %s" % (Qgis.QGIS_VERSION,  map_name))
        self.unsetCursor()
            
    def delete_map(self):
        name = self.ui.tabMaps.currentItem().text()
        map_id = self.ui.tabMaps.currentItem().data(Qt.UserRole)
        
        msgBox = QMessageBox()
        msgBox.setWindowTitle(self.tr("Delete QGIS Cloud map."))
        msgBox.setText(
            self.tr("Do you want to delete the map \"%s\"?") % name)
        msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
        msgBox.setDefaultButton(QMessageBox.Cancel)
        msgBox.setIcon(QMessageBox.Question)
        ret = msgBox.exec_()
        
        if ret == QMessageBox.Ok:
            self.ui.widgetServices.close()
            self.setCursor(Qt.WaitCursor)
            success = self.api.delete_map(map_id)
            
            if success:
                self.ui.btnMapDelete.setEnabled(False)
                self.refresh_maps()
            else:
                self.show_api_error(success)
                
            self.unsetCursor()
            self.ui.widgetServices.close()
        else:
            QMessageBox.warning(None,  self.tr('Warning'),  self.tr('Deletion of map "{name}" interrupted!').format(name=name))
            
            
    def refresh_maps(self):
        QApplication.setOverrideCursor(Qt.WaitCursor)
        if self.clouddb:
            map_list = self.api.read_maps()
            if self.show_api_error(map_list):
                QApplication.restoreOverrideCursor()
                return

            self.ui.tabMaps.clear()
            self.ui.btnMapDelete.setEnabled(False)
            self.ui.btnMapLoad.setEnabled(False)
            
            for map in map_list:
                it = QListWidgetItem(map['map']['name'])
                self.ui.tabMaps.addItem(it)
                it.setData(Qt.UserRole,  map['map']['id'])

        QApplication.restoreOverrideCursor()        

    def api_url(self):
        return str(self.ui.editServer.text())
        
    def update_urls(self,  map=None):
        
        if map == None:
            map = self.map()
        
        try:
            map = map.decode('utf-8')
        except:
            pass
            
        self.update_url(self.ui.lblWebmap, self.api_url(),
                        'https://', u'{0}/{1}/'.format(self.user, map))
                        
        if self.clouddb:
            self.update_url(
                self.ui.lblWMS, self.api_url(),
                'https://wms.', u'{0}/{1}/'.format(self.user, map))
        else:
            self.update_url(self.ui.lblWMS, self.api_url(
            ), 'https://', u'{0}/{1}/wms'.format(self.user, map))
        self.update_url(self.ui.lblMaps, self.api_url(), 'https://', 'maps')
        self.ui.widgetServices.show()

    def update_url(self, label, api_url, prefix, path):
        try:
            base_url = string.replace(api_url, 'https://api.', prefix)
        except:
            base_url = api_url.replace('https://api.', prefix)
            
        url = u'{0}/{1}'.format(base_url, path)
        text = re.sub(r'http[^"]+', url, str(label.text()))
        label.setText(text)

    def read_maps(self):
        if self.check_login():
            self.api.read_maps()

    def check_project_saved(self):
        project = QgsProject.instance()
        fname = project.fileName()
        if project.isDirty() or fname == '':
            msgBox = QMessageBox()
            msgBox.setWindowTitle(self.tr("Project Modified"))
            msgBox.setText(self.tr("The project has been modified."))
            msgBox.setInformativeText(
                self.tr("The project needs to be saved before it can be published. Proceed?"))
            msgBox.setStandardButtons(QMessageBox.Save | QMessageBox.Cancel)
            msgBox.setDefaultButton(QMessageBox.Save)
            if msgBox.exec_() == QMessageBox.Save:
                self.iface.actionSaveProject().trigger()
                return not project.isDirty()
            else:
                return False
        return True

    def publish_map(self):
        QApplication.setOverrideCursor(Qt.WaitCursor)
        canvas = self.iface.mapCanvas()
        srs=QgsMapSettings().destinationCrs()
        
        if "USER" in srs.authid():
            QMessageBox.warning(None, self.tr('Warning!'),  self.tr("The project has a user defined CRS. The use of user defined CRS is not supported. Please correct the project CRS before publishing!"))
            QApplication.restoreOverrideCursor()
            return 
            
        layer_dict = QgsProject.instance().mapLayers()
        layers = list(layer_dict.values())                    
        layerList = ''
        
        for layer in layers:
            if "USER" in layer.crs().authid():
                 layerList += "'"+layer.name()+"' "
        
        if len(layerList) > 0:
            QMessageBox.warning(None, self.tr('Warning!'),  self.tr("The layer(s) {layerlist}have user defined CRS. The use of user defined CRS is not supported. Please correct the CRS before publishing!").format(layerlist=layerList))
            QApplication.restoreOverrideCursor()
            return
                 
        saved = self.check_project_saved()
        if not saved:
            self.statusBar().showMessage(self.tr("Cancelled"))
        elif self.check_login() and self.check_layers():
            self.statusBar().showMessage(self.tr("Publishing map"))
            try:
                fullExtent = self.iface.mapCanvas().fullExtent()
                config = {
                    'fullExtent': {
                        'xmin': fullExtent.xMinimum(),
                        'ymin': fullExtent.yMinimum(),
                        'xmax': fullExtent.xMaximum(),
                        'ymax': fullExtent.yMaximum()
                        #},
                        # 'svgPaths': QgsApplication.svgPaths() #For resolving absolute symbol paths in print composer
                    }
                }
                
                fname = str(QgsProject.instance().fileName())
                    
                map = self.api.create_map(self.map(), fname, config)['map']
                self.show_api_error(map)
                if map['config']['missingSvgSymbols']:
                    self.publish_symbols(map['config']['missingSvgSymbols'])
                self.update_urls()
                self._push_message(self.tr("QGIS Cloud"), self.tr(
                    "Map successfully published"), level=0, duration=2)
                self.statusBar().showMessage(
                    self.tr("Map successfully published"))
            except Exception as e:
                self.statusBar().showMessage("")
                ErrorReportDialog(self.tr("Error uploading project"), self.tr("An error occured."), str(e) + "\n" + traceback.format_exc(), self.user, self).exec_()
        self.refresh_maps()
        QApplication.restoreOverrideCursor()

    def publish_symbols(self, missingSvgSymbols):
        self.statusBar().showMessage(self.tr("Uploading SVG symbols"))
        search_paths = QgsApplication.svgPaths()
        if hasattr(QgsProject.instance(), 'homePath'):
            search_paths += [QgsProject.instance().homePath()]
        # Search and upload symbol files
        for sym in missingSvgSymbols:
            # Absolute custom path
            if os.path.isfile(sym):
                self.api.create_graphic(sym, sym)
            else:
                for path in search_paths:
                    fullpath = os.path.join(str(path), sym)
                    if os.path.isfile(fullpath):
                        self.api.create_graphic(sym, fullpath)
        self.statusBar().showMessage("")

    def reset_load_data(self):
        self.ui.widgetServices.hide()
        self.update_local_data_sources([],  [])
        self.ui.btnUploadData.setEnabled(False)
        self.ui.tabMaps.clearSelection()

    def remove_layer(self, layer_id):
        if self.do_update_local_data_sources:
            # skip layer if layer will be removed
            self.update_local_layers(layer_id)
            self.activate_upload_button()

    def add_layer(self):
        if self.do_update_local_data_sources:
            self.update_local_layers()
            self.activate_upload_button()

    def update_local_layers(self, skip_layer_id=None):
        local_layers, unsupported_layers,  local_raster_layers = self.local_data_sources.local_layers(skip_layer_id)
        try:
            self.update_local_data_sources(local_layers,  local_raster_layers)
        except Exception as e:
            ErrorReportDialog(self.tr("Error checking local data sources"), self.tr("An error occured."), str(e) + "\n" + traceback.format_exc(), self.user, self).exec_()

        return local_layers, unsupported_layers,  local_raster_layers

    def check_layers(self):
        local_layers, unsupported_layers,  local_raster_layers = self.update_local_layers()
        
        
        if ((local_layers or local_raster_layers) and self.clouddb) or unsupported_layers:
            message = ""

            if local_layers or local_raster_layers:
                title = self.tr("Local layers found")
                message += self.tr(
                    "Some layers are using local data. Please upload local layers to your cloud database in the 'Upload Data' tab before publishing.\n\n")

            if unsupported_layers:
                title = self.tr("Unsupported layers found")
                message += self.tr(
                    "Raster, plugin or geometryless layers are not supported:\n\n")
                layer_types = ["No geometry", "Raster", "Plugin"]
                for layer in sorted(unsupported_layers, key=lambda layer: layer.name()):
                    message += self.tr("  -  %s (%s)\n") % (
                        layer.name(), layer_types[layer.type()])
                message += self.tr(
                    "\nPlease remove or replace above layers before publishing your map.\n")
                message += self.tr(
                    "For raster data you can use public WMS layers or the OpenLayers Plugin.")

            QMessageBox.warning(self, title, message)

            self.refresh_databases()
            self.ui.tabWidget.setCurrentWidget(self.ui.uploadTab)
            return False

        return True

    def update_local_data_sources(self, local_layers,  local_raster_layers):
        # update table names lookup
        local_layers += local_raster_layers
        self.update_data_sources_table_names()
        
        self.local_data_sources.update_local_data_sources(local_layers)

        # update GUI
        self.ui.tblLocalLayers.setRowCount(0)
        
        for data_source, layers in list(self.local_data_sources.iteritems()):
            layer_names = []
            for layer in layers:
                layer_names.append(str(layer.name()))
            layers_item = QTableWidgetItem(", ".join(layer_names))
            layers_item.setToolTip("\n".join(layer_names))
            data_source_item = QTableWidgetItem(data_source)
            data_source_item.setToolTip(data_source)
            # find a better table name if there are multiple layers with same
            # data source?
            table_name = layers[0].name()
            if data_source in self.data_sources_table_names:
                # use current table name if available to keep changes by user
                table_name = self.data_sources_table_names[data_source]
            
            table_name_item = QTableWidgetItem(self.launder_pg_name(table_name))
        
            if layers[0].providerType() == 'gdal':
                geometry_type_item = QTableWidgetItem('Raster')
            else:
                wkbType = layers[0].wkbType()
                
                if wkbType not in self.GEOMETRY_TYPES:
                    QMessageBox.warning(self.iface.mainWindow(), self.tr("Unsupported geometry type"), self.tr(
                        "Unsupported geometry type '{type}' in layer '{layer}'").format(type=self.__wkbTypeString(wkbType), layer=layers[0].name()))
                    continue
                geometry_type_item = QTableWidgetItem(self.GEOMETRY_TYPES[wkbType])
                if layers[0].providerType() == "ogr":
                    geometry_type_item.setToolTip(
                        self.tr("Note: OGR features will be converted to MULTI-type"))
                        
            srid_item = QTableWidgetItem(layers[0].crs().authid())
    
            row = self.ui.tblLocalLayers.rowCount()
            self.ui.tblLocalLayers.insertRow(row)
            self.ui.tblLocalLayers.setItem(
                row, self.COLUMN_LAYERS, layers_item)
            self.ui.tblLocalLayers.setItem(
                row, self.COLUMN_DATA_SOURCE, data_source_item)
            self.ui.tblLocalLayers.setItem(
                row, self.COLUMN_TABLE_NAME, table_name_item)
            self.ui.tblLocalLayers.setItem(
                row, self.COLUMN_GEOMETRY_TYPE, geometry_type_item)
            self.ui.tblLocalLayers.setItem(row, self.COLUMN_SRID, srid_item)

        if self.local_data_sources.count() > 0:
            self.ui.tblLocalLayers.resizeColumnsToContents()
            self.ui.tblLocalLayers.setColumnWidth(self.COLUMN_LAYERS, 100)
            self.ui.tblLocalLayers.setColumnWidth(self.COLUMN_DATA_SOURCE, 100)
            self.ui.tblLocalLayers.sortItems(self.COLUMN_DATA_SOURCE)
            self.ui.tblLocalLayers.setSortingEnabled(False)
        else:
            self.ui.btnUploadData.setEnabled(False)

        self.statusBar().showMessage(self.tr("Updated local data sources"))

    def __wkbTypeString(self, wkbType):
        if wkbType == Qgis.WkbUnknown:
            return "WkbUnknown"
        elif wkbType == Qgis.WkbPoint:
            return "WkbPoint"
        elif wkbType == Qgis.WkbLineString:
            return "WkbLineString"
        elif wkbType == Qgis.WkbMultiLineString:
            return "WkbMultiLineString"
        elif wkbType == Qgis.WkbPolygon:
            return "WkbPolygon"
        elif wkbType == Qgis.WkbMultiPoint:
            return "WkbMultiPoint"
        elif wkbType == Qgis.WkbMultiPolygon:
            return "WkbMultiPolygon"
        elif wkbType == Qgis.WkbNoGeometry:
            return "WkbNoGeometry"
        elif wkbType == Qgis.WkbPoint25D:
            return "WkbPoint25D"
        elif wkbType == Qgis.WkbLineString25D:
            return "WkbLineString25D"
        elif wkbType == Qgis.WkbPolygon25D:
            return "WkbPolygon25D"
        elif wkbType == Qgis.WkbMultiPoint25D:
            return "WkbMultiPoint25D"
        elif wkbType == Qgis.WkbMultiLineString25D:
            return "WkbMultiLineString25D"
        elif wkbType == Qgis.WkbMultiPolygon25D:
            return "WkbMultiPolygon25D"
        elif wkbType == QgsWkbTypes.LineStringZM:
            return "WkbLineStringZM"
        elif wkbType == QgsWkbTypes.MultiLineStringZM:
            return "WkbMultiLineStringZM"
        
        return self.tr("Unknown type")

#    @staticmethod
    def launder_pg_name(self,  name):
        # OGRPGDataSource::LaunderName
        # return re.sub(r"[#'-]", '_', unicode(name).lower())
        input_string = str(name).lower().encode('ascii', 'replace')
        input_string = input_string.replace(b" ",b"_")    
        input_string = input_string.replace(b".",b"_")    

        # check if table_name starts with number
        
        if re.search("^\d", input_string.decode('utf-8')):
           input_string = '_'+input_string.decode('utf-8') 
        
        try:
            return input_string.decode('utf-8')
        except:
            return input_string

    def refresh_local_data_sources(self):
        self.do_update_local_data_sources = True
        self.update_local_layers()
        self.activate_upload_button()
#
    def update_data_sources_table_names(self):
        if self.local_data_sources.count() == 0:
            self.data_sources_table_names.clear()
        else:
            # remove table names without data sources
            keys_to_remove = []
            for key in list(self.data_sources_table_names.keys()):
                if self.local_data_sources.layers(key) is None:
                    keys_to_remove.append(key)

            for key in keys_to_remove:
                del self.data_sources_table_names[key]

            # update table names
            for row in range(0, self.ui.tblLocalLayers.rowCount()):
                data_source = self.ui.tblLocalLayers.item(row, self.COLUMN_DATA_SOURCE).text()
                table_name = self.ui.tblLocalLayers.item(row, self.COLUMN_TABLE_NAME).text()
                self.data_sources_table_names[data_source] = table_name

    def activate_upload_button(self):
        if not self.storage_exceeded:
            self.ui.btnUploadData.setEnabled(self.local_data_sources.count() > 0)
            self.ui.btnPublishMap.setDisabled(self.storage_exceeded)
        else:
            self.ui.btnUploadData.setDisabled(self.storage_exceeded)
            self.ui.btnPublishMap.setDisabled(self.storage_exceeded)

    def upload_data(self):
        if self.check_login():
            if self.local_data_sources.count() == 0:
                return

            if self.db_connections.count() == 0:
                QMessageBox.warning(self, self.tr("No database available"), self.tr("Please create a database in the 'Account' tab."))
                return

            if not self.ui.cbUploadDatabase.currentIndex() >= 0:
                QMessageBox.warning(self, self.tr("No database selected"), self.tr("Please select a database to upload data."))
                return

            db_name = self.ui.cbUploadDatabase.currentText()

            if not self.db_connections.isPortOpen(db_name):
                uri = self.db_connections.cloud_layer_uri(db_name, "", "")
                host = str(uri.host())
                port = uri.port()
                QMessageBox.critical(self, self.tr("Network Error"),
                                     self.tr("Could not connect to database server ({0}) on port {1}. Please contact your system administrator or internet provider to open port {1} in the firewall".format(host, port)))
                return

            # disable update of local data sources during upload, as there are
            # temporary layers added and removed
            self.do_update_local_data_sources = False
            self.statusBar().showMessage(self.tr("Uploading data..."))
            self.setCursor(Qt.WaitCursor)
            self.ui.btnUploadData.hide()
            self.ui.spinner.start()
            self.ui.progressWidget.show()

            # Map<data_source, {table: table, layers: layers}>
            data_sources_items = {}
            for row in range(0, self.ui.tblLocalLayers.rowCount()):
                data_source = unicode(
                    self.ui.tblLocalLayers.item(
                        row, self.COLUMN_DATA_SOURCE).text())
                layers = self.local_data_sources.layers(data_source)
                if layers is not None:
                    table_name = unicode(
                        self.ui.tblLocalLayers.item(
                            row, self.COLUMN_TABLE_NAME).text())
                    data_sources_items[data_source] = {
                        u'table': unicode(table_name), u'layers': layers}

            login_info = self.api.check_login(version_info=self._version_info())
            try:            
                self.maxSize = login_info['max_storage']
                self.maxDBs = login_info['max_dbs']
            except:
                self.maxSize = 50
                self.maxDBs = 5

            try:
                self.data_upload.upload(self.db_connections.db(unicode(db_name)), data_sources_items, unicode(self.maxSize))
                upload_ok = True
            except Exception as e:
                ErrorReportDialog(self.tr("Upload errors occurred"), self.tr("Upload errors occurred. Not all data could be uploaded."), str(e) + "\n" + traceback.format_exc(), self.user, self).exec_()
                upload_ok = False

            self.ui.spinner.stop()
            self.ui.progressWidget.hide()
            self.ui.btnUploadData.show()
            self.unsetCursor()
            self.statusBar().showMessage("")
            # Refresh local layers
            self.do_update_local_data_sources = True
            self.update_local_layers()
            # Refresh used space after upload
            self.db_size(self.db_connections)

            if upload_ok:
                # Show save project dialog
                save_dialog = QDialog(self)
                save_dialog.setWindowTitle(self.tr("Save Project"))
                save_dialog.setLayout(QVBoxLayout())
                header = QWidget()
                header.setLayout(QVBoxLayout())
                label = QLabel(self.tr("Upload complete. The local layers in the project were replaced with the layers uploaded to the qgiscloud database."))
                label.setWordWrap(True)
                header.layout().addWidget(label)
                label = QLabel(self.tr("Choose were to save the modified project:"))
                label.setWordWrap(True)
                header.layout().addWidget(label)
                save_dialog.layout().setContentsMargins(0, 0, 0, 0)
                save_dialog.layout().addWidget(header)
                initialPath = QgsProject.instance().fileName()
                if not initialPath:
                    initialPath = QSettings().value("/UI/lastProjectDir", ".")
                fd = QFileDialog(None, self.tr("Save Project"), initialPath, "%s (*.qgz *.qgs)" % self.tr("QGIS Project Files"))
                fd.setParent(save_dialog, Qt.Widget)
                fd.setOption(QFileDialog.DontUseNativeDialog)
                fd.setAcceptMode(QFileDialog.AcceptSave)
                save_dialog.layout().addWidget(fd)
                header.layout().setContentsMargins(fd.layout().contentsMargins())
                fd.accepted.connect(save_dialog.accept)
                fd.rejected.connect(save_dialog.reject)
                if save_dialog.exec_() == QDialog.Accepted:
                    files = list(fd.selectedFiles())
                    if files:
                        QgsProject.instance().setFileName(files[0])
                        self.iface.actionSaveProject().trigger()

                # Switch to map tab
                self.ui.tabWidget.setCurrentWidget(self.ui.mapTab)

    def _push_message(self, title, text, level=0, duration=0):
        # QGIS >= 2.0
        if hasattr(self.iface, 'messageBar') and hasattr(self.iface.messageBar(), 'pushMessage'):
            self.iface.messageBar().pushMessage(title, text, level, duration)
        else:
            QMessageBox.information(self, title, text)

    def show_api_error(self, result):
        if 'error' in result:
            QMessageBox.critical(
                self, self.tr("QGIS Cloud Error"), "%s" % result['error'])
            self.statusBar().showMessage(self.tr("Error"))
            return True
        else:
            return False

#    def tr_uni(self, str):
#        return str(self.tr(str))

    def db_size(self,  db_connections):
        usedSpace = 0
        self.numDbs = len(list(db_connections._dbs.keys()))
        for db in list(db_connections._dbs.keys()):
            try:
                conn = db_connections.db(db).psycopg_connection()
            except:
                continue
            cursor = conn.cursor()
            sql = "SELECT pg_database_size('" + str(db) + "')"
            cursor.execute(sql)
            usedSpace += int(cursor.fetchone()[0])-(11*1024*1024)
            cursor.close()
            conn.close

        # Used space in MB
        usedSpace /= 1024 * 1024

        login_info = self.api.check_login(version_info=self._version_info())
        
        try:            
            self.maxSize = login_info['max_storage']
            self.maxDBs = login_info['max_dbs']
        except:
            self.maxSize = 50
            self.maxDBs = 5

        lblPalette = QPalette(self.ui.lblDbSize.palette())
        usage = usedSpace / float(self.maxSize)
        self.storage_exceeded = False
    
        if usage < 0.8:
            bg_color = QColor(255, 0, 0, 0)
            text_color = QColor(Qt.black)
        elif usage >= 0.8 and usage < 1:
            bg_color = QColor(255, 0, 0, 100)
            text_color = QColor(Qt.white)
        elif usage >= 1:
            bg_color = QColor(255, 0, 0, 255)
            text_color = QColor(Qt.white)
            self.storage_exceeded = True

        lblPalette.setColor(QPalette.Window, QColor(bg_color))
        lblPalette.setColor(QPalette.Foreground,QColor(text_color))
        
        self.ui.lblDbSize.setAutoFillBackground(True)
        self.ui.lblDbSize.setPalette(lblPalette)
        self.ui.lblDbSize.setText(
            self.tr("Used DB Storage: ") + "%d / %d MB" % (usedSpace, self.maxSize))

        self.ui.lblDbSizeUpload.setAutoFillBackground(True)
        self.ui.lblDbSizeUpload.setPalette(lblPalette)
        self.ui.lblDbSizeUpload.setText(
            self.tr("Used DB Storage: ") + "%d / %d MB" % (usedSpace, self.maxSize))
        self.ui.btnUploadData.setDisabled(self.storage_exceeded)
        self.ui.btnPublishMap.setDisabled(self.storage_exceeded)
class QgisCloudPluginDialog(QDockWidget):
    COLUMN_LAYERS = 0
    COLUMN_SCHEMA_NAME = 1
    COLUMN_TABLE_NAME = 2
    COLUMN_GEOMETRY_TYPE = 3
    COLUMN_SRID = 4
    COLUMN_DATA_SOURCE = 5


    GEOMETRY_TYPES = {
        QgsWkbTypes.Unknown: "Unknown",
        QgsWkbTypes.NoGeometry: "No geometry",
        QgsWkbTypes.Point: "Point",
        QgsWkbTypes.MultiPoint: "MultiPoint",               
        QgsWkbTypes.PointZ: "PointZ",
        QgsWkbTypes.MultiPointZ: "MultiPointZ",
        QgsWkbTypes.PointM: "PointM",
        QgsWkbTypes.MultiPointM: "MultiPointM",
        QgsWkbTypes.PointZM: "PointZM",
        QgsWkbTypes.MultiPointZM: "MultiPointZM",
        QgsWkbTypes.Point25D: "Point25D",
        QgsWkbTypes.MultiPoint25D: "MultiPoint25D",
        QgsWkbTypes.LineString: "LineString",
        QgsWkbTypes.MultiLineString: "LineString",
        QgsWkbTypes.LineStringZ: "LineStringZ",
        QgsWkbTypes.MultiLineStringZ: "LineStringZ",
        QgsWkbTypes.LineStringM: "LineStringM",
        QgsWkbTypes.MultiLineStringM: "LineStringM",
        QgsWkbTypes.LineStringZM: "LineStringZM",
        QgsWkbTypes.MultiLineStringZM: "LineStringZM",
        QgsWkbTypes.LineString25D: "LineString25D",
        QgsWkbTypes.MultiLineString25D: "MultiLineString25D",
        QgsWkbTypes.Polygon: "Polygon",            
        QgsWkbTypes.MultiPolygon: "MultiPolygon", 
        QgsWkbTypes.PolygonZ: "PolygonZ",            
        QgsWkbTypes.MultiPolygonZ: "MultiPolygonZ", 
        QgsWkbTypes.PolygonM: "PolygonM",            
        QgsWkbTypes.MultiPolygonM: "MultiPolygonM", 
        QgsWkbTypes.PolygonZM: "PolygonZM",            
        QgsWkbTypes.MultiPolygonZM: "MultiPolygonZM", 
        QgsWkbTypes.Polygon25D: "Polygon25D",            
        QgsWkbTypes.MultiPolygon25D: "MultiPolygon25D", 
        QgsWkbTypes.CircularString: "CircularString",
        QgsWkbTypes.CompoundCurve: "CompoundCurve",
        QgsWkbTypes.CurvePolygon: "CurvePolygon", 
        QgsWkbTypes.MultiCurve: "MultiCurve",
        QgsWkbTypes.MultiSurface: "MultiSurface",
        QgsWkbTypes.CircularStringZ: "CircularStringZ",
        QgsWkbTypes.CompoundCurveZ: "CompoundCurveZ",
        QgsWkbTypes.CurvePolygonZ: "CurvePolygonZ", 
        QgsWkbTypes.MultiCurveZ: "MultiCurveZ",
        QgsWkbTypes.MultiSurfaceZ: "MultiSurfaceZ",
        QgsWkbTypes.CircularStringM: "CircularStringM",
        QgsWkbTypes.CompoundCurveM: "CompoundCurveM",
        QgsWkbTypes.CurvePolygonM: "CurvePolygonM", 
        QgsWkbTypes.MultiCurveM: "MultiCurveM",
        QgsWkbTypes.MultiSurfaceM: "MultiSurfaceM",
        QgsWkbTypes.CircularStringZM: "CircularStringZM",
        QgsWkbTypes.CompoundCurveZM: "CompoundCurveZM",
        QgsWkbTypes.CurvePolygonZM: "CurvePolygonZM", 
        QgsWkbTypes.MultiCurveZM: "MultiCurveZM",
        QgsWkbTypes.MultiSurfaceZM: "MultiSurfaceZM",
    }
        
    PROJECT_INSTANCE = QgsProject.instance()     
    
    
    def __init__(self, iface, version):
        QDockWidget.__init__(self, None)
        self.iface = iface
        self.clouddb = True
        self.version = version
        # Set up the user interface from Designer.
        self.ui = Ui_QgisCloudPlugin()
        self.ui.setupUi(self)
        self.storage_exceeded = True
        
        myAbout = DlgAbout()
        self.ui.aboutText.setText(
            myAbout.aboutString() +
            myAbout.contribString() +
            myAbout.licenseString() +
            "<p>Versions:<ul>" +
            "<li>QGIS: %s</li>" % str(Qgis.QGIS_VERSION).encode("utf-8") +
            "<li>Python: %s</li>" % sys.version.replace("\n", " ") +
            "<li>OS: %s</li>" % platform.platform() +
            "</ul></p>")
            
        data_protection_link = """<a href="http://qgiscloud.com/pages/privacy">%s</a>""" % (self.tr("Privacy Policy"))
            
        self.ui.lblVersionPlugin.setText("%s &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;%s" % (self.version,  data_protection_link))
        self.ui.lblVersionPlugin.setOpenExternalLinks(True)            

        self.ui.tblLocalLayers.setColumnCount(6)
        header = ["Layers","Table Schema","Table name", "Geometry type", "SRID","Data Source"]
        self.ui.tblLocalLayers.setHorizontalHeaderLabels(header)
        self.ui.tblLocalLayers.resizeColumnsToContents()
#        self.ui.tblLocalLayers.setEditTriggers(QAbstractItemView.NoEditTriggers)
        
        self.ui.btnUploadData.setEnabled(False)
        self.ui.btnPublishMap.setEnabled(False)
        self.ui.btnMapDelete.setEnabled(False)
        self.ui.progressWidget.hide()
        self.ui.btnLogout.hide()
        self.ui.lblLoginStatus.hide()
        self.ui.widgetServices.hide()
        self.ui.widgetDatabases.setEnabled(False)
        self.ui.widgetMaps.setEnabled(False)
        self.ui.labelOpenLayersPlugin.hide()

        try:
            from .openlayers_menu import OpenlayersMenu
            self.ui.btnBackgroundLayer.setMenu(OpenlayersMenu(self.iface))
        except:
                self.ui.btnBackgroundLayer.hide()
                self.ui.labelOpenLayersPlugin.show()

        # map<data source, table name>
        self.data_sources_table_names = {}
        # flag to disable update of local data sources during upload
        self.do_update_local_data_sources = True

        self.ui.btnLogin.clicked.connect(self.check_login)
        self.ui.btnDbCreate.clicked.connect(self.create_database)
        self.ui.btnDbDelete.clicked.connect(self.delete_database)
        self.ui.btnDbRefresh.clicked.connect(self.refresh_databases)
        self.ui.btnMapDelete.clicked.connect(self.delete_map)
        self.ui.btnMapLoad.clicked.connect(self.map_load)
        self.ui.tabMaps.itemDoubleClicked.connect(self.map_load)
        self.ui.tabDatabases.itemSelectionChanged.connect(self.select_database)
        self.ui.tabMaps.itemSelectionChanged.connect(self.select_map)
        self.ui.btnPublishMap.clicked.connect(self.publish_map)
        self.ui.btnRefreshLocalLayers.clicked.connect(self.refresh_local_data_sources)
        self.ui.cbUploadDatabase.currentTextChanged.connect(self.update_data_sources_table_names)
        self.iface.newProjectCreated.connect(self.reset_load_data)
        self.iface.projectRead.connect(self.reset_load_data)

        self.PROJECT_INSTANCE.layerWillBeRemoved.connect(self.remove_layer)
        self.PROJECT_INSTANCE.layerWasAdded.connect(self.add_layer)
        
        self.ui.cbUploadDatabase.currentIndexChanged.connect(lambda idx: self.activate_upload_button())
        self.ui.btnUploadData.clicked.connect(self.upload_data)

        self.ui.editServer.textChanged.connect(self.serverURL)
        self.ui.resetUrlBtn.clicked.connect(self.resetApiUrl)

        self.read_settings()
        self.api = API()
        self.db_connections = DbConnections()
        self.local_data_sources = LocalDataSources()
        self.data_upload = DataUpload(
            self.iface, self.statusBar(), self.ui.lblProgress, self.api,
            self.db_connections)

        if self.URL == "":
            self.ui.editServer.setText(self.api.api_url())
        else:
            self.ui.editServer.setText(self.URL)

        self.palette_red = QPalette(self.ui.lblVersionPlugin.palette())
        self.palette_red.setColor(QPalette.WindowText, Qt.red)

    def unload(self):
        self.do_update_local_data_sources = False
        if self.iface:
            self.iface.newProjectCreated.disconnect(self.reset_load_data)
            self.iface.projectRead.disconnect(self.reset_load_data)
        
        if self.PROJECT_INSTANCE:
            self.PROJECT_INSTANCE.layerWillBeRemoved.disconnect(self.remove_layer)
            self.PROJECT_INSTANCE.layerWasAdded.disconnect(self.add_layer)
        

    def statusBar(self):
        return self.iface.mainWindow().statusBar()

    def map(self):
        project = QgsProject.instance()
        name = os.path.splitext(os.path.basename(str(project.fileName())))[0]
            
        # Allowed chars for QGISCloud map name: /\A[A-Za-z0-9\_\-]*\Z/
#        name = str(name).encode('ascii', 'replace')  # Replace non-ascii chars
        name = str(re.sub(r'[^\x00-\x7F]+','_', name))
        # Replace withespace
        name = name.replace(" ",  "_")
        return name

    def resetApiUrl(self):
        self.ui.editServer.setText(self.api.api_url())

    def serverURL(self, URL):
        self.URL = URL
        self.store_settings()

    def store_settings(self):
        s = QSettings()
        s.setValue("qgiscloud/user", self.user)
        s.setValue("qgiscloud/URL", self.URL)

    def read_settings(self):
        s = QSettings()
        self.user = s.value("qgiscloud/user", "")
        self.URL = s.value("qgiscloud/URL", "")

    def _update_clouddb_mode(self, clouddb):
        self.clouddb = clouddb
        self.ui.widgetDatabases.setVisible(self.clouddb)
        tab_index = 1
        tab_name = QApplication.translate("QgisCloudPlugin", "Upload Data")
        visible = (self.ui.tabWidget.indexOf(self.ui.uploadTab) == tab_index)
        if visible and not self.clouddb:
            self.ui.tabWidget.removeTab(tab_index)
        elif not visible and self.clouddb:
            self.ui.tabWidget.insertTab(tab_index, self.ui.uploadTab, tab_name)

    def _version_info(self):
        return {
            'versions': {
                'plugin': self.version.encode('utf-8').decode('utf-8'),
                'QGIS': Qgis.QGIS_VERSION.encode('utf-8').decode('utf-8'),
                'OS': platform.platform().encode('utf-8').decode('utf-8'),
                'Python': sys.version.encode('utf-8').decode('utf-8')
            }
        }
        
    def _qgis3_info(self):
        cloud_info_off = QSettings().value("Plugin-QgisCloud/qgis3_info_off", defaultValue=False, type=bool)
        lastInfo = QSettings().value("Plugin-QgisCloud/qgis3_info_ts", defaultValue=0.0, type=float)
        
        day = 3600*24
        now = time.time()
        days = (now-lastInfo)/day
        
        if days >= 20 or not cloud_info_off:
            self.qgis3_info_dlg = Qgis3Warning()
            self.qgis3_info_dlg.show()
            QSettings().setValue("Plugin-QgisCloud/qgis3_info_ts", now)

    def check_login(self):
        version_ok = True
        if not self.api.check_auth():
            self._qgis3_info()

            login_dialog = QDialog(self)
            login_dialog.ui = Ui_LoginDialog()
            login_dialog.ui.setupUi(login_dialog)
            login_dialog.ui.editUser.setText(self.user)
            login_ok = False
            while not login_ok and version_ok:
                self.api.set_url(self.api_url())
                if not login_dialog.exec_():
                    self.api.set_auth(
                        user=login_dialog.ui.editUser.text(), password=None)
                    return login_ok
                self.api.set_auth(
                    user=login_dialog.ui.editUser.text(),
                    password=login_dialog.ui.editPassword.text())
                try:
                    login_info = self.api.check_login(
                        version_info=self._version_info())

# QGIS private Cloud has no tos_accepted
                    try:
                        if not login_info['tos_accepted']:
                            result = QMessageBox.information(
                                None,
                                self.tr("Accept new Privacy Policy"),
                                self.tr("""Due to the GDPR qgiscloud.com has a new <a href='http://qgiscloud.com/en/pages/privacy'> Privacy Policy </a>. 
                                To continue using qgiscloud.com, you must accept the new policy. """),
                                QMessageBox.StandardButtons(
                                    QMessageBox.No |
                                    QMessageBox.Yes))
                            
                            if result == QMessageBox.No:
                                login_ok = False
                                return
                            else:
                                result = self.api.accept_tos()
                    except:
                        pass
                            
                    self.user = login_dialog.ui.editUser.text()
                    self._update_clouddb_mode(login_info['clouddb'])
                    version_ok = StrictVersion(self.version) >= StrictVersion(login_info['current_plugin'])
                    if not version_ok:
                        self.ui.lblVersionPlugin.setPalette(self.palette_red)
                        QMessageBox.information(None, self.tr('New Version'),  self.tr('New plugin release {version} is available! Please upgrade the QGIS Cloud plugin.').format(version=login_info['current_plugin']))
                    self.store_settings()
                    self.ui.btnLogin.hide()
                    self.ui.lblSignup.hide()
                    self.ui.btnLogout.show()
                    self.ui.widgetDatabases.setEnabled(True)
                    self.ui.widgetMaps.setEnabled(True)

                    self.ui.lblLoginStatus.setText(
                        self.tr("Logged in as {0} ({1})").format(self.user, login_info['plan']))
                    self.ui.lblLoginStatus.show()
                    self._push_message(
                        self.tr("QGIS Cloud"),
                        self.tr("Logged in as {0}").format(self.user),
                        level=0, duration=2)
                    self.refresh_databases()
                    self.refresh_maps()
                    if not version_ok:
                        self._push_message(self.tr("QGIS Cloud"), self.tr(
                            "Unsupported versions detected. Please check your versions first!"), level=1)
                        version_ok = False
                        self.ui.tabWidget.setCurrentWidget(self.ui.aboutTab)
                    login_ok = True
                    self.update_local_layers()
            
                except ForbiddenError:
                    QMessageBox.critical(
                        self, self.tr("Account Disabled"),
                        self.tr("Account {username} is disabled! Please contact [email protected]").format(username=login_dialog.ui.editUser.text()))
                    login_ok = False                    
                except UnauthorizedError:
                    QMessageBox.critical(
                        self, self.tr("Login for user {username} failed").format(username=login_dialog.ui.editUser.text()),
                        self.tr("Wrong user name or password"))
                    login_ok = False
                except (TokenRequiredError, ConnectionException) as e:
                    QMessageBox.critical(
                        self, self.tr("Login failed"),
                        self.tr("Login failed: %s") % str(e))
                    login_ok = False
        return version_ok

    def create_database(self):
        if self.numDbs < self.maxDBs:
            db = self.api.create_database()
            self.show_api_error(db)
            self.refresh_databases()
        else:
            QMessageBox.warning(None, self.tr('Warning!'),  self.tr('Number of %s permitted databases exceeded! Please upgrade your account!') % self.maxDBs)

    def delete_database(self):
        name = self.ui.tabDatabases.currentItem().text()
        
        answer = False
        
        for layer in list(self.PROJECT_INSTANCE.mapLayers().values()):            
            if QgsDataSourceUri(layer.publicSource()).database() == name:
                
                if not answer:
                    answer = QMessageBox.question(
                        self,
                        self.tr("Warning"),
                        self.tr('You have layers from database "{name}" loaded in your project! Do you want to remove them before you delete database "{name}"?').format(name=name),
                        QMessageBox.StandardButtons(
                            QMessageBox.Cancel |
                            QMessageBox.Yes))

                if answer == QMessageBox.Yes:
                     self.PROJECT_INSTANCE.removeMapLayer(layer.id())
                     
        if answer == QMessageBox.Cancel:
            QMessageBox.warning(None,  self.tr('Warning'),  self.tr('Deletion of database "{name}" interrupted!').format(name=name))
            return
            
        msgBox = QMessageBox()
        msgBox.setWindowTitle(self.tr("Delete QGIS Cloud database."))
        msgBox.setText(
            self.tr("Do you want to delete the database \"%s\"?") % name)
        msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
        msgBox.setDefaultButton(QMessageBox.Cancel)
        msgBox.setIcon(QMessageBox.Question)
        ret = msgBox.exec_()
        
        if ret == QMessageBox.Ok:
            self.setCursor(Qt.WaitCursor)
            result = self.api.delete_database(name)
            self.show_api_error(result)
            self.ui.btnDbDelete.setEnabled(False)
            time.sleep(2)
            self.refresh_databases()
            self.unsetCursor()

    def select_database(self):
        self.ui.btnDbDelete.setEnabled(len(self.ui.tabDatabases.selectedItems()) > 0)
            
    def select_map(self):
        self.ui.btnMapDelete.setEnabled(len(self.ui.tabMaps.selectedItems()) > 0)
        self.ui.btnMapLoad.setEnabled(len(self.ui.tabMaps.selectedItems()) > 0)
        self.update_urls(map=self.ui.tabMaps.currentItem().text())

    @pyqtSlot()
    def on_btnLogout_clicked(self):
        self.api.reset_auth()
        self.ui.btnLogout.hide()
        self.ui.lblLoginStatus.hide()
        self.ui.btnLogin.show()
        self.ui.widgetServices.hide()
        self.ui.tabDatabases.clear()
        self.ui.tabMaps.clear()
        self.ui.lblDbSize.setText("")
        self.ui.lblDbSizeUpload.setText("")
        self.ui.cbUploadDatabase.clear()
        self.ui.widgetDatabases.setEnabled(False)
        self.activate_upload_button()

    def refresh_databases(self):
        QApplication.setOverrideCursor(Qt.WaitCursor)
        if self.clouddb:
            db_list = self.api.read_databases()
            if self.show_api_error(db_list):
                QApplication.restoreOverrideCursor()
                return
            self.db_connections = DbConnections()
            for db in db_list:
                self.db_connections.add_from_json(db)

            self.ui.tabDatabases.clear()
            self.ui.btnDbDelete.setEnabled(False)
            self.ui.cbUploadDatabase.clear()
            self.ui.cbUploadDatabase.setEditable(True)
            self.ui.cbUploadDatabase.lineEdit().setReadOnly(True)
            if self.db_connections.count() == 0:
                self.ui.cbUploadDatabase.setEditText(self.tr("No databases"))
            else:
                for name, db in list(self.db_connections.iteritems()):
                    it = QListWidgetItem(name)
                    it.setToolTip(db.description())
                    self.ui.tabDatabases.addItem(it)
                    self.ui.cbUploadDatabase.addItem(name)
                if self.ui.cbUploadDatabase.count() > 1:
                    # Display the "Select database" text if more than one db is available
                    self.ui.cbUploadDatabase.setCurrentIndex(-1)
                    self.ui.cbUploadDatabase.setEditText(self.tr("Select database"))
            self.db_connections.refresh(self.user)

        self.db_size(self.db_connections)
        QApplication.restoreOverrideCursor()
        
    def map_load(self,  item=None,  row=None):     
        self.ui.widgetServices.close()
        self.setCursor(Qt.WaitCursor)
        map_id = self.ui.tabMaps.currentItem().data(Qt.UserRole)
        map_name = self.ui.tabMaps.currentItem().text()
        result = self.api.load_map_project(map_name,  map_id)
        qgs_file_name = '%s/%s.qgs' % (tempfile.gettempdir(), map_name)
        qgs_file = open(qgs_file_name,  'w')
        qgs_file.write(result)
        qgs_file.close()
        project = QgsProject.instance()
        if project.isDirty():
            ok = QMessageBox.information(
                self,
                self.tr("Save Project"),
                self.tr("""Your actual project has changes. Do you want to save the project?"""),
                QMessageBox.StandardButtons(
                    QMessageBox.Abort |
                    QMessageBox.Discard |
                    QMessageBox.Save))
            if ok:
                project.write()           
                
        project.read(qgs_file_name)
        project.setDirty(False)
        self.iface.mainWindow().setWindowTitle("QGIS %s - %s" % (Qgis.QGIS_VERSION,  map_name))
        self.unsetCursor()
            
    def delete_map(self):
        name = self.ui.tabMaps.currentItem().text()
        map_id = self.ui.tabMaps.currentItem().data(Qt.UserRole)
        
        msgBox = QMessageBox()
        msgBox.setWindowTitle(self.tr("Delete QGIS Cloud map."))
        msgBox.setText(
            self.tr("Do you want to delete the map \"%s\"?") % name)
        msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
        msgBox.setDefaultButton(QMessageBox.Cancel)
        msgBox.setIcon(QMessageBox.Question)
        ret = msgBox.exec_()
        
        if ret == QMessageBox.Ok:
            self.ui.widgetServices.close()
            self.setCursor(Qt.WaitCursor)
            success = self.api.delete_map(map_id)
            
            if success:
                self.ui.btnMapDelete.setEnabled(False)
                self.refresh_maps()
            else:
                self.show_api_error(success)
                
            self.unsetCursor()
            self.ui.widgetServices.close()
        else:
            QMessageBox.warning(None,  self.tr('Warning'),  self.tr('Deletion of map "{name}" interrupted!').format(name=name))
            
            
    def refresh_maps(self):
        QApplication.setOverrideCursor(Qt.WaitCursor)
        if self.clouddb:
            map_list = self.api.read_maps()
            if self.show_api_error(map_list):
                QApplication.restoreOverrideCursor()
                return

            self.ui.tabMaps.clear()
            self.ui.btnMapDelete.setEnabled(False)
            self.ui.btnMapLoad.setEnabled(False)
            
            for map in map_list:
                it = QListWidgetItem(map['map']['name'])
                self.ui.tabMaps.addItem(it)
                it.setData(Qt.UserRole,  map['map']['id'])

        QApplication.restoreOverrideCursor()        

    def api_url(self):
        return str(self.ui.editServer.text())
        
    def update_urls(self,  map=None):
        
        if map == None:
            map = self.map()
        
        try:
            map = map.decode('utf-8')
        except:
            pass
            
        self.update_url(self.ui.lblWebmap, self.api_url(),
                        'https://', u'{0}/{1}/'.format(self.user, map))
                        
        if self.clouddb:
            self.update_url(
                self.ui.lblWMS, self.api_url(),
                'https://wms.', u'{0}/{1}/'.format(self.user, map))
        else:
            self.update_url(self.ui.lblWMS, self.api_url(
            ), 'https://', u'{0}/{1}/wms'.format(self.user, map))
        self.update_url(self.ui.lblMaps, self.api_url(), 'https://', 'maps')
        self.ui.widgetServices.show()

    def update_url(self, label, api_url, prefix, path):
        try:
            base_url = string.replace(api_url, 'https://api.', prefix)
        except:
            base_url = api_url.replace('https://api.', prefix)
            
        url = u'{0}/{1}'.format(base_url, path)
        text = re.sub(r'http[^"]+', url, str(label.text()))
        label.setText(text)

    def read_maps(self):
        if self.check_login():
            self.api.read_maps()

    def check_project_saved(self):
        project = QgsProject.instance()
        fname = project.fileName()
        if project.isDirty() or fname == '':
            msgBox = QMessageBox()
            msgBox.setWindowTitle(self.tr("Project Modified"))
            msgBox.setText(self.tr("The project has been modified."))
            msgBox.setInformativeText(
                self.tr("The project needs to be saved before it can be published. Proceed?"))
            msgBox.setStandardButtons(QMessageBox.Save | QMessageBox.Cancel)
            msgBox.setDefaultButton(QMessageBox.Save)
            if msgBox.exec_() == QMessageBox.Save:
                self.iface.actionSaveProject().trigger()
                return not project.isDirty()
            else:
                return False
        return True

    def publish_map(self):
        QApplication.setOverrideCursor(Qt.WaitCursor)
        srs=QgsMapSettings().destinationCrs()
        
        if "USER" in srs.authid():
            QMessageBox.warning(None, self.tr('Warning!'),  self.tr("The project has a user defined CRS. The use of user defined CRS is not supported. Please correct the project CRS before publishing!"))
            QApplication.restoreOverrideCursor()
            return 
            
        layer_dict = QgsProject.instance().mapLayers()
        layers = list(layer_dict.values())                    
        layerList = ''
        
        for layer in layers:
            if "USER" in layer.crs().authid():
                 layerList += "'"+layer.name()+"' "
        
        if len(layerList) > 0:
            QMessageBox.warning(None, self.tr('Warning!'),  self.tr("The layer(s) {layerlist}have user defined CRS. The use of user defined CRS is not supported. Please correct the CRS before publishing!").format(layerlist=layerList))
            QApplication.restoreOverrideCursor()
            return
                 
        saved = self.check_project_saved()
        if not saved:
            self.statusBar().showMessage(self.tr("Cancelled"))
        elif self.check_login() and self.check_layers():
            self.statusBar().showMessage(self.tr("Publishing map"))
            try:
                fullExtent = self.iface.mapCanvas().fullExtent()
                config = {
                    'fullExtent': {
                        'xmin': fullExtent.xMinimum(),
                        'ymin': fullExtent.yMinimum(),
                        'xmax': fullExtent.xMaximum(),
                        'ymax': fullExtent.yMaximum()
                        #},
                        # 'svgPaths': QgsApplication.svgPaths() #For resolving absolute symbol paths in print composer
                    }
                }
                
                fname = str(QgsProject.instance().fileName())
                map = self.api.create_map( self.map(), fname, config)['map']
                self.show_api_error(map)
                if map['config']['missingSvgSymbols']:
                    self.publish_symbols(map['config']['missingSvgSymbols'])
                self.update_urls()
                self._push_message(self.tr("QGIS Cloud"), self.tr(
                    "Map successfully published"), level=0, duration=2)
                self.statusBar().showMessage(
                    self.tr("Map successfully published"))
            except Exception as e:
                self.statusBar().showMessage("")
                ErrorReportDialog(self.tr("Error uploading project"), self.tr("An error occured."), str(e) + "\n" + traceback.format_exc(), self.user, self).exec_()
        self.refresh_maps()
        QApplication.restoreOverrideCursor()

    def publish_symbols(self, missingSvgSymbols):
        self.statusBar().showMessage(self.tr("Uploading SVG symbols"))
        search_paths = QgsApplication.svgPaths()
        if hasattr(QgsProject.instance(), 'homePath'):
            search_paths += [QgsProject.instance().homePath()]
        # Search and upload symbol files
        for sym in missingSvgSymbols:
            # Absolute custom path
            if os.path.isfile(sym):
                self.api.create_graphic(sym, sym)
            else:
                for path in search_paths:
                    fullpath = os.path.join(str(path), sym)
                    if os.path.isfile(fullpath):
                        self.api.create_graphic(sym, fullpath)
        self.statusBar().showMessage("")

    def reset_load_data(self):
        self.ui.widgetServices.hide()
        self.update_local_data_sources([],  [])
        self.ui.btnUploadData.setEnabled(False)
        self.ui.tabMaps.clearSelection()

    def remove_layer(self, layer_id):
        if self.do_update_local_data_sources:
            # skip layer if layer will be removed
            self.update_local_layers(layer_id)
            self.activate_upload_button()

    def add_layer(self):
        if self.do_update_local_data_sources:
            self.update_local_layers()
            self.activate_upload_button()

    def update_local_layers(self, skip_layer_id=None):
        local_layers, unsupported_layers,  local_raster_layers = self.local_data_sources.local_layers(skip_layer_id)
        try:
            self.update_local_data_sources(local_layers,  local_raster_layers)
        except Exception as e:
            ErrorReportDialog(self.tr("Error checking local data sources"), self.tr("An error occured."), str(e) + "\n" + traceback.format_exc(), self.user, self).exec_()

        return local_layers, unsupported_layers,  local_raster_layers

    def check_layers(self):
        local_layers, unsupported_layers,  local_raster_layers = self.update_local_layers()
        
        
        if ((local_layers or local_raster_layers) and self.clouddb) or unsupported_layers:
            message = ""

            if local_layers or local_raster_layers:
                title = self.tr("Local layers found")
                message += self.tr(
                    "Some layers are using local data. Please upload local layers to your cloud database in the 'Upload Data' tab before publishing.\n\n")

            if unsupported_layers:
                title = self.tr("Unsupported layers found")
                message += self.tr(
                    "Raster, plugin or geometryless layers are not supported:\n\n")
                layer_types = ["No geometry", "Raster", "Plugin"]
                for layer in sorted(unsupported_layers, key=lambda layer: layer.name()):
                    message += self.tr("  -  %s (%s)\n") % (
                        layer.name(), layer_types[layer.type()])
                message += self.tr(
                    "\nPlease remove or replace above layers before publishing your map.\n")
                message += self.tr(
                    "For raster data you can use public WMS layers or the OpenLayers Plugin.")

            QMessageBox.warning(self, title, message)

            self.refresh_databases()
            self.ui.tabWidget.setCurrentWidget(self.ui.uploadTab)
            return False

        return True

    def update_local_data_sources(self, local_layers,  local_raster_layers):
        # update table names lookup
        local_layers += local_raster_layers
        self.update_data_sources_table_names()
        
        self.local_data_sources.update_local_data_sources(local_layers)

        # update GUI
        self.ui.tblLocalLayers.setRowCount(0)
        
        schema_list = []
        if self.ui.cbUploadDatabase.count() == 1:
            schema_list = self.fetch_schemas(self.ui.cbUploadDatabase.currentText())
        elif self.ui.cbUploadDatabase.currentIndex() > 0:
            schema_list = self.fetch_schemas(self.ui.cbUploadDatabase.currentText())

        for data_source, layers in list(self.local_data_sources.iteritems()):
            layer_names = []
            for layer in layers:
                layer_names.append(str(layer.name()))
            layers_item = QTableWidgetItem(", ".join(layer_names))
            layers_item.setToolTip("\n".join(layer_names))
            data_source_item = QTableWidgetItem(data_source)
            data_source_item.setToolTip(data_source)            

            # find a better table name if there are multiple layers with same
            # data source?
            table_name = layers[0].name()
            if data_source in self.data_sources_table_names:
                # use current table name if available to keep changes by user
                table_name = self.data_sources_table_names[data_source]
            
            table_name_item = QTableWidgetItem(self.launder_pg_name(table_name))
        
            if layers[0].providerType() == 'gdal':
                geometry_type_item = QTableWidgetItem('Raster')
            else:
                wkbType = layers[0].wkbType()
                
                if wkbType not in self.GEOMETRY_TYPES:
                    QMessageBox.warning(self.iface.mainWindow(), self.tr("Unsupported geometry type"), self.tr(
                        "Unsupported geometry type '{type}' in layer '{layer}'").format(type=self.__wkbTypeString(wkbType), layer=layers[0].name()))
                    continue
                geometry_type_item = QTableWidgetItem(self.GEOMETRY_TYPES[wkbType])
                if layers[0].providerType() == "ogr":
                    geometry_type_item.setToolTip(
                        self.tr("Note: OGR features will be converted to MULTI-type"))
                        
            srid_item = QTableWidgetItem(layers[0].crs().authid())

            row = self.ui.tblLocalLayers.rowCount()
            self.ui.tblLocalLayers.insertRow(row)
            layers_item.setFlags( Qt.ItemIsSelectable |  Qt.ItemIsEnabled )
            self.ui.tblLocalLayers.setItem(
                row, self.COLUMN_LAYERS, layers_item)
            data_source_item.setFlags( Qt.ItemIsSelectable |  Qt.ItemIsEnabled )
            self.ui.tblLocalLayers.setItem(
                row, self.COLUMN_DATA_SOURCE, data_source_item)                
                
# create combo box in schema column filled with all schema names of the selected database               
            cmb_schema = QComboBox()
            cmb_schema.setEditable(True)
            cmb_schema.addItems(schema_list)
            self.ui.tblLocalLayers.setCellWidget(row, self.COLUMN_SCHEMA_NAME, cmb_schema)

            table_name_item.setFlags( Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled )
            self.ui.tblLocalLayers.setItem(
                row, self.COLUMN_TABLE_NAME, table_name_item)

            geometry_type_item.setFlags( Qt.ItemIsSelectable |  Qt.ItemIsEnabled )
            self.ui.tblLocalLayers.setItem(
                row, self.COLUMN_GEOMETRY_TYPE, geometry_type_item)
                
            srid_item.setFlags( Qt.ItemIsSelectable |  Qt.ItemIsEnabled )
            self.ui.tblLocalLayers.setItem(row, self.COLUMN_SRID, srid_item)

        if self.local_data_sources.count() > 0:
            self.ui.tblLocalLayers.resizeColumnsToContents()
            self.ui.tblLocalLayers.setColumnWidth(self.COLUMN_LAYERS, 100)
            self.ui.tblLocalLayers.setColumnWidth(self.COLUMN_DATA_SOURCE, 100)
            self.ui.tblLocalLayers.sortItems(self.COLUMN_DATA_SOURCE)
            self.ui.tblLocalLayers.setSortingEnabled(False)
        else:
            self.ui.btnUploadData.setEnabled(False)

        self.statusBar().showMessage(self.tr("Updated local data sources"))

    def __wkbTypeString(self, wkbType):
        if wkbType == QgsWkbTypes.Unknown:
            return "WkbUnknown"
        elif wkbType == QgsWkbTypes.Point:
            return "WkbPoint"
        elif wkbType == QgsWkbTypes.LineString:
            return "WkbLineString"
        elif wkbType == QgsWkbTypes.MultiLineString:
            return "WkbMultiLineString"
        elif wkbType == QgsWkbTypes.Polygon:
            return "WkbPolygon"
        elif wkbType == QgsWkbTypes.MultiPoint:
            return "WkbMultiPoint"
        elif wkbType == QgsWkbTypes.MultiPolygon:
            return "WkbMultiPolygon"
        elif wkbType == QgsWkbTypes.NoGeometry:
            return "WkbNoGeometry"
        elif wkbType == QgsWkbTypes.Point25D:
            return "WkbPoint25D"
        elif wkbType == QgsWkbTypes.LineString25D:
            return "WkbLineString25D"
        elif wkbType == QgsWkbTypes.Polygon25D:
            return "WkbPolygon25D"
        elif wkbType == QgsWkbTypes.MultiPoint25D:
            return "WkbMultiPoint25D"
        elif wkbType == QgsWkbTypes.MultiLineString25D:
            return "WkbMultiLineString25D"
        elif wkbType == QgsWkbTypes.MultiPolygon25D:
            return "WkbMultiPolygon25D"
        elif wkbType == QgsWkbTypes.LineStringZM:
            return "WkbLineStringZM"
        elif wkbType == QgsWkbTypes.MultiLineStringZM:
            return "WkbMultiLineStringZM"
        
        return self.tr("Unknown type")

#    @staticmethod
    def launder_pg_name(self,  name):
        # OGRPGDataSource::LaunderName
        # return re.sub(r"[#'-]", '_', unicode(name).lower())
        input_string = str(name).lower().encode('ascii', 'replace')
        input_string = input_string.replace(b" ",b"_")    
        input_string = input_string.replace(b".",b"_")    

        # check if table_name starts with number
        
        if re.search("^\d", input_string.decode('utf-8')):
           input_string = '_'+input_string.decode('utf-8') 
        
        try:
            return input_string.decode('utf-8')
        except:
            return input_string

    def refresh_local_data_sources(self):
        self.do_update_local_data_sources = True
        self.update_local_layers()
        self.activate_upload_button()
        
#
    def update_data_sources_table_names(self):
        
        schema_list = []
        schema_list = self.fetch_schemas(self.ui.cbUploadDatabase.currentText())
            
        if self.local_data_sources.count() == 0:
            self.data_sources_table_names.clear()
        else:
            # remove table names without data sources
            keys_to_remove = []
            for key in list(self.data_sources_table_names.keys()):
                if self.local_data_sources.layers(key) is None:
                    keys_to_remove.append(key)

            for key in keys_to_remove:
                del self.data_sources_table_names[key]

            # update table names
            if schema_list != None:
                for row in range(0, self.ui.tblLocalLayers.rowCount()):
                    data_source = self.ui.tblLocalLayers.item(row, self.COLUMN_DATA_SOURCE).text()
                    cmb_schema = QComboBox()
                    cmb_schema.setEditable(True)
                    cmb_schema.addItems(schema_list)
                    self.ui.tblLocalLayers.setCellWidget(row, self.COLUMN_SCHEMA_NAME, cmb_schema)                
                    table_name = self.ui.tblLocalLayers.item(row, self.COLUMN_TABLE_NAME).text()
                    self.data_sources_table_names[data_source] = table_name

    def activate_upload_button(self):
        if not self.storage_exceeded:
            self.ui.btnUploadData.setEnabled(self.local_data_sources.count() > 0)
            self.ui.btnPublishMap.setDisabled(self.storage_exceeded)
        else:
            self.ui.btnUploadData.setDisabled(self.storage_exceeded)
            self.ui.btnPublishMap.setDisabled(self.storage_exceeded)

    def upload_data(self):
        if self.check_login():
            if self.local_data_sources.count() == 0:
                return

            if self.db_connections.count() == 0:
                QMessageBox.warning(self, self.tr("No database available"), self.tr("Please create a database in the 'Account' tab."))
                return

            if not self.ui.cbUploadDatabase.currentIndex() >= 0:
                QMessageBox.warning(self, self.tr("No database selected"), self.tr("Please select a database to upload data."))
                return

            db_name = self.ui.cbUploadDatabase.currentText()

            if not self.db_connections.isPortOpen(db_name):
                uri = self.db_connections.cloud_layer_uri(db_name, "", "")
                host = str(uri.host())
                port = uri.port()
                QMessageBox.critical(self, self.tr("Network Error"),
                                     self.tr("Could not connect to database server ({0}) on port {1}. Please contact your system administrator or internet provider to open port {1} in the firewall".format(host, port)))
                return

            # disable update of local data sources during upload, as there are
            # temporary layers added and removed
            self.do_update_local_data_sources = False
            self.statusBar().showMessage(self.tr("Uploading data..."))
            self.setCursor(Qt.WaitCursor)
            self.ui.btnUploadData.hide()
            self.ui.spinner.start()
            self.ui.progressWidget.show()

            # Map<data_source, {schema: schema, table: table, layers: layers}>
            data_sources_items = {}
            for row in range(0, self.ui.tblLocalLayers.rowCount()):
                data_source = unicode(
                    self.ui.tblLocalLayers.item(
                        row, self.COLUMN_DATA_SOURCE).text())
                layers = self.local_data_sources.layers(data_source)
                if layers is not None:
                    schema_name = unicode(
                        self.ui.tblLocalLayers.cellWidget(
                            row, self.COLUMN_SCHEMA_NAME).currentText())                    
                    table_name = unicode(
                        self.ui.tblLocalLayers.item(
                            row, self.COLUMN_TABLE_NAME).text())
                    data_sources_items[data_source] = {
                        u'schema': unicode(schema_name), u'table': unicode(table_name), u'layers': layers}

            login_info = self.api.check_login(version_info=self._version_info())
            try:            
                self.maxSize = login_info['max_storage']
                self.maxDBs = login_info['max_dbs']
            except:
                self.maxSize = 50
                self.maxDBs = 5

            try:
                self.data_upload.upload(self.db_connections.db(unicode(db_name)), data_sources_items, unicode(self.maxSize))
                upload_ok = True
            except Exception as e:
                ErrorReportDialog(self.tr("Upload errors occurred"), self.tr("Upload errors occurred. Not all data could be uploaded."), str(e) + "\n" + traceback.format_exc(), self.user, self).exec_()
                upload_ok = False

            self.ui.spinner.stop()
            self.ui.progressWidget.hide()
            self.ui.btnUploadData.show()
            self.unsetCursor()
            self.statusBar().showMessage("")
            # Refresh local layers
            self.do_update_local_data_sources = True
            self.update_local_layers()
            # Refresh used space after upload
            self.db_size(self.db_connections)

            if upload_ok:
                # Show save project dialog
                save_dialog = QDialog(self)
                save_dialog.setWindowTitle(self.tr("Save Project"))
                save_dialog.setLayout(QVBoxLayout())
                header = QWidget()
                header.setLayout(QVBoxLayout())
                label = QLabel(self.tr("Upload complete. The local layers in the project were replaced with the layers uploaded to the qgiscloud database."))
                label.setWordWrap(True)
                header.layout().addWidget(label)
                label = QLabel(self.tr("Choose were to save the modified project:"))
                label.setWordWrap(True)
                header.layout().addWidget(label)
                save_dialog.layout().setContentsMargins(0, 0, 0, 0)
                save_dialog.layout().addWidget(header)
                initialPath = QgsProject.instance().fileName()
                if not initialPath:
                    initialPath = QSettings().value("/UI/lastProjectDir", ".")
                fd = QFileDialog(None, self.tr("Save Project"), initialPath, "%s (*.qgz *.qgs)" % self.tr("QGIS Project Files"))
                fd.setParent(save_dialog, Qt.Widget)
                fd.setOption(QFileDialog.DontUseNativeDialog)
                fd.setAcceptMode(QFileDialog.AcceptSave)
                save_dialog.layout().addWidget(fd)
                header.layout().setContentsMargins(fd.layout().contentsMargins())
                fd.accepted.connect(save_dialog.accept)
                fd.rejected.connect(save_dialog.reject)
                if save_dialog.exec_() == QDialog.Accepted:
                    files = list(fd.selectedFiles())
                    if files:
                        QgsProject.instance().setFileName(files[0])
                        self.iface.actionSaveProject().trigger()

                # Switch to map tab
                self.ui.tabWidget.setCurrentWidget(self.ui.mapTab)

    def _push_message(self, title, text, level=0, duration=0):
        # QGIS >= 2.0
        if hasattr(self.iface, 'messageBar') and hasattr(self.iface.messageBar(), 'pushMessage'):
            self.iface.messageBar().pushMessage(title, text, level, duration)
        else:
            QMessageBox.information(self, title, text)

    def show_api_error(self, result):
        if 'error' in result:
            QMessageBox.critical(
                self, self.tr("QGIS Cloud Error"), "%s" % result['error'])
            self.statusBar().showMessage(self.tr("Error"))
            return True
        else:
            return False


    @pyqtSlot(str)
    def on_cmb_schema_currentIndexChanged(self, p0):
        """
        Slot documentation goes here.
        
        @param p0 DESCRIPTION
        @type str
        """
        # TODO: not implemented yet
        self.fetch_schemas(p0)
        
        
    def fetch_schemas(self,  db):
        if db != '' and self.ui.cbUploadDatabase.currentIndex() >= 0:
            conn = self.db_connections.db(db).psycopg_connection()
            cursor = conn.cursor()
            sql = """
                    select *
                    from pg_catalog.pg_namespace
                    where nspowner <> 10
                      and nspname <> 'topology'
            """
            schema_list = []
            schema_list.append('public')
            cursor.execute(sql)
            for record in cursor:
                schema_list.append(list(record)[0])
            cursor.close()
            conn.close
            return schema_list
        else:
            return None

    def db_size(self,  db_connections):
        usedSpace = 0
        self.numDbs = len(list(db_connections._dbs.keys()))
        for db in list(db_connections._dbs.keys()):
            try:
                conn = db_connections.db(db).psycopg_connection()
            except:
                continue
            cursor = conn.cursor()
            sql = "SELECT pg_database_size('" + str(db) + "')"
            cursor.execute(sql)
            usedSpace += int(cursor.fetchone()[0])-(11*1024*1024)
            cursor.close()
            conn.close

        # Used space in MB
        usedSpace /= 1024 * 1024

        login_info = self.api.check_login(version_info=self._version_info())
        
        try:            
            self.maxSize = login_info['max_storage']
            self.maxDBs = login_info['max_dbs']
        except:
            self.maxSize = 50
            self.maxDBs = 5

        lblPalette = QPalette(self.ui.lblDbSize.palette())
        usage = usedSpace / float(self.maxSize)
        self.storage_exceeded = False
    
        if usage < 0.8:
            bg_color = QColor(255, 0, 0, 0)
            text_color = QColor(Qt.black)
        elif usage >= 0.8 and usage < 1:
            bg_color = QColor(255, 0, 0, 100)
            text_color = QColor(Qt.white)
        elif usage >= 1:
            bg_color = QColor(255, 0, 0, 255)
            text_color = QColor(Qt.white)
            self.storage_exceeded = True

        lblPalette.setColor(QPalette.Window, QColor(bg_color))
        lblPalette.setColor(QPalette.Foreground,QColor(text_color))
        
        self.ui.lblDbSize.setAutoFillBackground(True)
        self.ui.lblDbSize.setPalette(lblPalette)
        self.ui.lblDbSize.setText(
            self.tr("Used DB Storage: ") + "%d / %d MB" % (usedSpace, self.maxSize))

        self.ui.lblDbSizeUpload.setAutoFillBackground(True)
        self.ui.lblDbSizeUpload.setPalette(lblPalette)
        self.ui.lblDbSizeUpload.setText(
            self.tr("Used DB Storage: ") + "%d / %d MB" % (usedSpace, self.maxSize))
        self.ui.btnUploadData.setDisabled(self.storage_exceeded)
        self.ui.btnPublishMap.setDisabled(self.storage_exceeded)
Beispiel #13
0
    def __init__(self, parent=None):

        QWidget.__init__(self, parent)
        palette = QPalette(self.palette())
        palette.setColor(palette.Background, Qt.transparent)
        self.setPalette(palette)