Beispiel #1
0
 def __init__(self):
     """
     Default constructor.
     """
     self._text_editor = TextEditor()
     self._memento_list = []
     self._prev_state_ptr = -1
Beispiel #2
0
    def view_xml(self, m=None):
        if not self.dock_window:
            self.dock_window = TextEditor(self)
            self.iface.mainWindow().addDockWidget(Qt.BottomDockWidgetArea,
                                                  self.dock_window)
            if not self.using_mapnik:
                # http://trac.osgeo.org/qgis/changeset/12955 - render starting signal
                QObject.connect(self.canvas,
                                SIGNAL("renderComplete(QPainter *)"),
                                self.checkLayers)

        self.dock_window.show()
        if self.loaded_mapfile:
            # if we have loaded a map xml or mml
            # so lets just display the active file
            xml = open(self.loaded_mapfile, 'rb').read()
        else:
            if not m:
                # regenerate from qgis objects
                e_c = sync.EasyCanvas(self, self.canvas)
                m = e_c.to_mapnik()
            if hasattr(mapnik, 'save_map_to_string'):
                xml = mapnik.save_map_to_string(m)
            else:
                (handle, mapfile) = tempfile.mkstemp('.xml', 'quantumnik-map-')
                os.close(handle)
                mapnik.save_map(m, str(mapfile))
                xml = open(mapfile, 'rb').read()
        e = self.canvas.extent()
        bbox = '%s %s %s %s' % (e.xMinimum(), e.yMinimum(), e.xMaximum(),
                                e.yMaximum())
        cmd = '\n<!-- nik2img.py mapnik.xml out.png -d %s %s -e %s -->\n' % (
            self.canvas.width(), self.canvas.height(), bbox)
        try:
            if self.mapnik_map:
                cmd += '<!-- <MinScaleDenominator>%s</MinScaleDenominator> -->\n' % (
                    self.mapnik_map.scale_denominator())
        except:
            pass

        code = xml + cmd
        if HIGHLIGHTING:
            highlighted = highlight(
                code, XmlLexer(),
                HtmlFormatter(linenos=False, nowrap=False, full=True))
            self.dock_window.textEdit.setHtml(highlighted)
        else:
            self.dock_window.textEdit.setText(xml + cmd)
Beispiel #3
0
 def text_editor ( self, ui, object, name, description, parent ):
     return TextEditor( parent,
                        factory     = self, 
                        ui          = ui, 
                        object      = object, 
                        name        = name, 
                        description = description ) 
Beispiel #4
0
    def test_symbols(self):
        self.app.simulate_typing('foo')
        self.app.simulate_keystroke(curses.ascii.ESC)  # quit

        self.app = TextEditor(self.args)  # re-open
        self.app.simulate_typing('~ ~')
        self.app.simulate_keystroke(curses.ascii.ESC)  # quit

        self.outfile.seek(0)
        self.assertEqual(self.outfile.read(), "~ ~foo")
 def __init__(self, msg_service: MessageService):
     self.doc = Doc()
     self.doc.site = int(random.getrandbits(32))
     self.patch_set = []
     self.msg_service = msg_service
     self.text_field = TextEditor(
         scrollbar=True,
         line_numbers=False,
         search_field=SearchToolbar(),
         key_bindings=self.__init_bindings(),
         lexer=AuthorLexer(self.doc)
     )
Beispiel #6
0
    def view_xml(self, m=None):
        if not self.dock_window:
            self.dock_window = TextEditor(self)
            self.iface.mainWindow().addDockWidget(Qt.BottomDockWidgetArea, self.dock_window)
            if not self.using_mapnik:
                # http://trac.osgeo.org/qgis/changeset/12955 - render starting signal
                QObject.connect(self.canvas, SIGNAL("renderComplete(QPainter *)"), self.checkLayers)

        self.dock_window.show()
        if self.loaded_mapfile:
            # if we have loaded a map xml or mml
            # so lets just display the active file
            xml = open(self.loaded_mapfile, "rb").read()
        else:
            if not m:
                # regenerate from qgis objects
                e_c = sync.EasyCanvas(self, self.canvas)
                m = e_c.to_mapnik()
            if hasattr(mapnik, "save_map_to_string"):
                xml = mapnik.save_map_to_string(m)
            else:
                (handle, mapfile) = tempfile.mkstemp(".xml", "quantumnik-map-")
                os.close(handle)
                mapnik.save_map(m, str(mapfile))
                xml = open(mapfile, "rb").read()
        e = self.canvas.extent()
        bbox = "%s %s %s %s" % (e.xMinimum(), e.yMinimum(), e.xMaximum(), e.yMaximum())
        cmd = "\n<!-- nik2img.py mapnik.xml out.png -d %s %s -e %s -->\n" % (
            self.canvas.width(),
            self.canvas.height(),
            bbox,
        )
        try:
            if self.mapnik_map:
                cmd += "<!-- <MinScaleDenominator>%s</MinScaleDenominator> -->\n" % (
                    self.mapnik_map.scale_denominator()
                )
        except:
            pass

        code = xml + cmd
        if HIGHLIGHTING:
            highlighted = highlight(code, XmlLexer(), HtmlFormatter(linenos=False, nowrap=False, full=True))
            self.dock_window.textEdit.setHtml(highlighted)
        else:
            self.dock_window.textEdit.setText(xml + cmd)
    def __init__(self, title="", label_text="", completer=None):
        self.future = Future()

        def accept_text(buf) -> bool:
            """
            Change focus to OK button after user pressed enter on text field.
            """
            get_app().layout.focus(ok_button)
            buf.complete_state = None
            return True

        def accept():
            """
            Set future result to text from text field if user pressed OK
            button.
            """
            self.future.set_result(self.text_area.text)

        def cancel():
            """
            Set future result to None if user pressed cancel button.
            """
            self.future.set_result(None)

        self.text_area = TextEditor(
            completer=completer,
            multiline=False,
            width=D(preferred=40),
            accept_handler=accept_text,
            key_bindings=load_key_bindings(),
        )

        ok_button = Button(text="OK", handler=accept)
        cancel_button = Button(text="Cancel", handler=cancel)

        self.dialog = Dialog(
            title=title,
            body=HSplit([Label(text=label_text), self.text_area]),
            buttons=[ok_button, cancel_button],
            width=D(preferred=80),
            modal=True,
        )
Beispiel #8
0
 def setUp(self):
     self.outfile = tempfile.NamedTemporaryFile()
     self.args = ['text_editor.py', self.outfile.name]
     self.app = TextEditor(self.args)
     super(TextEditorTest, self).setUp()
Beispiel #9
0
class TextEditorTest(AudioAppTest):
    def setUp(self):
        self.outfile = tempfile.NamedTemporaryFile()
        self.args = ['text_editor.py', self.outfile.name]
        self.app = TextEditor(self.args)
        super(TextEditorTest, self).setUp()

    def test_simple(self):
        # should speak previous word after non-alpha character
        self.app.simulate_typing('hello world ')
        self.assertJustSaid('world')

        # should speak character just deleted
        self.app.simulate_keystroke(curses.ascii.DEL)
        self.assertJustSaid('space')

        # should play tone when attempting to move cursor beyond bounds
        self.app.simulate_keystroke(curses.KEY_RIGHT)
        self.assertJustSaid('[tone]')

        for i in range(5):  # move cursor five characters left
            self.app.simulate_keystroke(curses.KEY_LEFT)
        self.app.simulate_typing('awesome ')

        self.app.simulate_keystroke(curses.ascii.ESC)  # quit
        self.outfile.seek(0)

        # check that contents were properly saved
        self.assertEqual(self.outfile.read(), "hello awesome world")

    def test_symbols(self):
        self.app.simulate_typing('foo')
        self.app.simulate_keystroke(curses.ascii.ESC)  # quit

        self.app = TextEditor(self.args)  # re-open
        self.app.simulate_typing('~ ~')
        self.app.simulate_keystroke(curses.ascii.ESC)  # quit

        self.outfile.seek(0)
        self.assertEqual(self.outfile.read(), "~ ~foo")

    def test_gibberish(self):
        # type n random printable characters
        n = 100
        self.app.simulate_typing(
            random.choice(string.printable) for i in range(n))
        self.app.simulate_keystroke(curses.ascii.ESC)  # quit
        self.outfile.seek(0)

        self.assertEqual(n, len(self.outfile.read()))

    def test_sentence(self):
        self.app.simulate_typing('Hello there. This is a second sentence.')
        self.assertJustSaid('this is a second sentence')
Beispiel #10
0
class Quantumnik(QObject):
    def __init__(self, iface):
        QObject.__init__(self)
        self.iface = iface
        self.canvas = iface.mapCanvas()

        # Fake canvas to use in tab to overlay the quantumnik layer
        self.qCanvas = None
        self.qCanvasPan = None
        self.qCanvasZoomIn = None
        self.qCanvasZoomOut = None
        self.tabWidget = None

        self.mapnik_map = None
        self.using_mapnik = False
        self.from_mapfile = False
        self.loaded_mapfile = None
        self.been_warned = False
        self.last_image_path = None
        self.dock_window = None
        self.keyAction = None
        self.keyAction2 = None
        self.keyAction3 = None

    def initGui(self):
        self.action = QAction(QIcon(":/mapnikglobe.png"), QString("Create Mapnik Canvas"),
                              self.iface.mainWindow())
        self.action.setWhatsThis("Create Mapnik Canvas")
        self.action.setStatusTip("%s: render with Mapnik" % NAME)
        QObject.connect(self.action, SIGNAL("triggered()"), self.toggle)

        self.action4 = QAction(QString("View live xml"), self.iface.mainWindow())
        QObject.connect(self.action4, SIGNAL("triggered()"), self.view_xml)

        self.action3 = QAction(QString("Export Mapnik xml"),
                               self.iface.mainWindow())
        QObject.connect(self.action3, SIGNAL("triggered()"), self.save_xml)

        self.action5 = QAction(QString("Load Mapnik xml"),
                               self.iface.mainWindow())
        QObject.connect(self.action5, SIGNAL("triggered()"), self.load_xml)

        self.action6 = QAction(QString("Load Cascadenik mml"),
                               self.iface.mainWindow())
        QObject.connect(self.action6, SIGNAL("triggered()"), self.load_mml)

        self.action7 = QAction(QString("Export Map Graphics"), self.iface.mainWindow())
        QObject.connect(self.action7, SIGNAL("triggered()"),
                        self.export_image_gui)

        self.helpaction = QAction(QIcon(":/mapnikhelp.png"),"About",
                                  self.iface.mainWindow())
        self.helpaction.setWhatsThis("%s Help" % NAME)
        QObject.connect(self.helpaction, SIGNAL("triggered()"), self.helprun)
        
        self.iface.addToolBarIcon(self.action)


        self.iface.addPluginToMenu("&%s" % NAME, self.action)
        self.iface.addPluginToMenu("&%s" % NAME, self.helpaction)
        self.iface.addPluginToMenu("&%s" % NAME, self.action3)
        self.iface.addPluginToMenu("&%s" % NAME, self.action4)
        self.iface.addPluginToMenu("&%s" % NAME, self.action5)
        self.iface.addPluginToMenu("&%s" % NAME, self.action6)
        self.iface.addPluginToMenu("&%s" % NAME, self.action7)

        # > QGIS 1.2
        if hasattr(self.iface,'registerMainWindowAction'):
            self.keyAction2 = QAction(QString("Switch to QGIS"), self.iface.mainWindow())
            self.iface.registerMainWindowAction(self.keyAction2, "Ctrl+[")
            self.iface.addPluginToMenu("&%s" % NAME, self.keyAction2)
            QObject.connect(self.keyAction2, SIGNAL("triggered()"),self.switch_tab_qgis)
    
            self.keyAction3 = QAction(QString("Switch to Mapnik"), self.iface.mainWindow())
            self.iface.registerMainWindowAction(self.keyAction3, "Ctrl+]")
            self.iface.addPluginToMenu("&%s" % NAME, self.keyAction3)
            QObject.connect(self.keyAction3, SIGNAL("triggered()"),self.switch_tab_mapnik)
        
    def unload(self):
        self.iface.removePluginMenu("&%s" % NAME,self.action)
        self.iface.removePluginMenu("&%s" % NAME,self.helpaction)
        self.iface.removePluginMenu("&%s" % NAME,self.action3)
        self.iface.removePluginMenu("&%s" % NAME,self.action4)
        self.iface.removePluginMenu("&%s" % NAME,self.action5)
        self.iface.removePluginMenu("&%s" % NAME,self.action6)
        self.iface.removePluginMenu("&%s" % NAME,self.action7)
        self.iface.removeToolBarIcon(self.action)
        if self.keyAction:
            self.iface.unregisterMainWindowAction(self.keyAction)
        if self.keyAction2:
            self.iface.unregisterMainWindowAction(self.keyAction2)
        if self.keyAction3:
            self.iface.unregisterMainWindowAction(self.keyAction3)

    def export_image_gui(self):
        flags = Qt.WindowTitleHint | Qt.WindowSystemMenuHint | Qt.WindowStaysOnTopHint
        export = ImageExport(self,flags)
        export.show()

    def view_xml(self,m=None):
        if not self.dock_window:
            self.dock_window = TextEditor(self)
            self.iface.mainWindow().addDockWidget( Qt.BottomDockWidgetArea,
                                                   self.dock_window )
            if not self.using_mapnik:
                # http://trac.osgeo.org/qgis/changeset/12955 - render starting signal
                QObject.connect(self.canvas, SIGNAL("renderComplete(QPainter *)"),
                                self.checkLayers)
        
        self.dock_window.show()
        if self.loaded_mapfile:
            # if we have loaded a map xml or mml
            # so lets just display the active file
            xml = open(self.loaded_mapfile,'rb').read()
        else:
            if not m:
                # regenerate from qgis objects
                e_c = sync.EasyCanvas(self,self.canvas)
                m = e_c.to_mapnik()
            if hasattr(mapnik,'save_map_to_string'):
                xml = mapnik.save_map_to_string(m)
            else:
                (handle, mapfile) = tempfile.mkstemp('.xml', 'quantumnik-map-')
                os.close(handle)
                mapnik.save_map(m,str(mapfile))
                xml = open(mapfile,'rb').read()
        e = self.canvas.extent()
        bbox = '%s %s %s %s' % (e.xMinimum(),e.yMinimum(),
                                e.xMaximum(),e.yMaximum())
        cmd = '\n<!-- nik2img.py mapnik.xml out.png -d %s %s -e %s -->\n' % (self.canvas.width(), self.canvas.height(), bbox)
        try:
            if self.mapnik_map:
                cmd += '<!-- <MinScaleDenominator>%s</MinScaleDenominator> -->\n' % (self.mapnik_map.scale_denominator())
        except:
            pass

        code = xml + cmd
        if HIGHLIGHTING:
            highlighted = highlight(code, XmlLexer(), HtmlFormatter(linenos=False, nowrap=False, full=True))
            self.dock_window.textEdit.setHtml(highlighted)
        else:
            self.dock_window.textEdit.setText(xml + cmd)

    def helprun(self):
        infoString = QString("Written by Dane Springmeyer\nhttps://github.com/springmeyer/quantumnik")
        QMessageBox.information(self.iface.mainWindow(),"About %s" % NAME,infoString)

    def toggle(self):
        if self.using_mapnik:
            self.stop_rendering()
        else:
            self.start_rendering()

    def proj_warning(self):
        self.been_warned = True
        ren = self.canvas.mapRenderer()
        if not ren.hasCrsTransformEnabled() and self.canvas.layerCount() > 1:
            if hasattr(self.canvas.layer(0),'crs'):
                if not self.canvas.layer(0).crs().toProj4() == ren.destinationCrs().toProj4():
                    QMessageBox.information(self.iface.mainWindow(),"Warning","The projection of the map and the first layer do not match. Mapnik may not render the layer(s) correctly.\n\nYou likely need to either enable 'On-the-fly' CRS transformation or set the Map projection in your Project Properties to the projection of your layer(s).")
            else:
                if not self.canvas.layer(0).srs().toProj4() == ren.destinationSrs().toProj4():
                    QMessageBox.information(self.iface.mainWindow(),"Warning","The projection of the map and the first layer do not match. Mapnik may not render the layer(s) correctly.\n\nYou likely need to either enable 'On-the-fly' CRS transformation or set the Map projection in your Project Properties to the projection of your layer(s).")
        
    def save_xml(self):
        # need to expose as an option!
        relative_paths = True
        
        mapfile = QFileDialog.getSaveFileName(None, "Save file dialog", 
                                              'mapnik.xml', "Mapfile (*.xml)")
        if mapfile:
            e_c = sync.EasyCanvas(self,self.canvas)
            mapfile_ = str(mapfile)
            base_path = os.path.dirname(mapfile_)
            e_c.base_path = base_path
            m = e_c.to_mapnik()
            mapnik.save_map(m,mapfile_)

            if relative_paths:
                relativism.fix_paths(mapfile_,base_path)
    
    def make_bundle(self): pass  
        # todo: accept directory name
        # move mapfile and all file based datasources
        # into that folder and stash some docs inside
        # provide option to zip and upload to url on the fly
        
    def set_canvas_from_mapnik(self):
        # set up keyboard shortcut
        # > QGIS 1.2
        if hasattr(self.iface,'registerMainWindowAction'):
            if not self.keyAction:
                # TODO - hotkey does not work on linux....
                self.keyAction = QAction(QString("Refresh " + NAME), self.iface.mainWindow())
                self.iface.registerMainWindowAction(self.keyAction, "Ctrl+r")
                self.iface.addPluginToMenu("&%s" % NAME, self.keyAction)
                QObject.connect(self.keyAction, SIGNAL("triggered()"),self.toggle)
        
        self.mapnik_map.zoom_all()
        e = self.mapnik_map.envelope()
        crs = QgsCoordinateReferenceSystem()
        srs = self.mapnik_map.srs
        if srs == '+init=epsg:900913':
            # until we can look it up in srs.db...
            merc = "+init=EPSG:900913"
            crs.createFromProj4(QString(merc))
        elif 'init' in srs:
            # TODO - quick hack, needs regex and fallbacks
            epsg = srs.split(':')[1]
            crs.createFromEpsg(int(epsg))
        else:
            if srs == '+proj=latlong +datum=WGS84':
                # expand the Mapnik srs a bit 
                # http://trac.mapnik.org/ticket/333
                srs = '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs'
            crs.createFromProj4(QString(srs))
        if hasattr(self.canvas.mapRenderer(),'setDestinationCrs'):
            self.canvas.mapRenderer().setDestinationCrs(crs)
        else:
            self.canvas.mapRenderer().setDestinationSrs(crs)
        if not crs.isValid():
            QMessageBox.information(self.iface.mainWindow(),
                                    "Warning","Projection not understood")
            return
        QObject.connect(self.canvas, SIGNAL("renderComplete(QPainter *)"),
                        self.render_dynamic)
        self.canvas.setExtent(QgsRectangle(e.minx,e.miny,e.maxx,e.maxy))
        self.canvas.refresh()

    def set_mapnik_to_canvas(self):
        QObject.connect(self.canvas, SIGNAL("renderComplete(QPainter *)"),
                        self.render_dynamic)
        self.canvas.refresh()
        
    def refresh_loaded_mapfile(self):
        if self.mapfile_format == 'Cascadenik mml':
            self.load_mml(refresh=True)
        else:
            self.load_xml(refresh=True)
        
    def load_mml(self,refresh=False):
        self.from_mapfile = True
        self.mapfile_format = 'Cascadenik mml'
        if self.loaded_mapfile and refresh:
            mapfile = self.loaded_mapfile
        else:
            mapfile = QFileDialog.getOpenFileName(None, "Open file dialog",
                                                  '', "Cascadenik MML (*.mml)")
        if mapfile:
            self.mapnik_map = mapnik.Map(1,1)
            import cascadenik
            if hasattr(cascadenik,'VERSION'):
                major = int(cascadenik.VERSION.split('.')[0])
                if major < 1:
                    from cascadenik import compile
                    compiled = '%s_compiled.xml' % os.path.splitext(str(mapfile))[0]
                    open(compiled, 'w').write(compile(str(mapfile)))
                    mapnik.load_map(self.mapnik_map, compiled)
                elif major == 1:
                    output_dir = os.path.dirname(str(mapfile))
                    cascadenik.load_map(self.mapnik_map,str(mapfile),output_dir,verbose=False)
                elif major > 1:
                    raise NotImplementedError('This nik2img version does not yet support Cascadenik > 1.x, please upgrade nik2img to the latest release')
            else:
                from cascadenik import compile
                compiled = '%s_compiled.xml' % os.path.splitext(str(mapfile))[0]
                #if os.path.exits(compiled):
                    #pass
                open(compiled, 'w').write(compile(str(mapfile)))
                mapnik.load_map(self.mapnik_map, compiled)

            if self.loaded_mapfile and refresh:
                self.set_mapnik_to_canvas()            
            else:
                self.set_canvas_from_mapnik()
            self.loaded_mapfile = str(mapfile)
  
    def load_xml(self,refresh=False):
        # TODO - consider putting into its own layer:
        # https://trac.osgeo.org/qgis/ticket/2392#comment:4
        self.from_mapfile = True
        self.mapfile_format = 'xml mapfile'
        if self.loaded_mapfile and refresh:
            mapfile = self.loaded_mapfile
        else:
            mapfile = QFileDialog.getOpenFileName(None, "Open file dialog",
                                                  '', "XML Mapfile (*.xml)")
        if mapfile:
            self.mapnik_map = mapnik.Map(1,1)
            mapnik.load_map(self.mapnik_map,str(mapfile))
            if self.loaded_mapfile and refresh:
                self.set_mapnik_to_canvas()            
            else:
                self.set_canvas_from_mapnik()
            self.loaded_mapfile = str(mapfile)
  
    def finishStopRender(self):
        self.iface.mapCanvas().setMinimumSize(QSize(0, 0))

    def stop_rendering(self):
        # Disconnect all the signals as we disable the tool
        QObject.disconnect(self.qCanvas, SIGNAL("renderComplete(QPainter *)"),
                           self.render_dynamic)
        QObject.disconnect(self.qCanvas,
                           SIGNAL("xyCoordinates(const QgsPoint&)"),
                           self.updateCoordsDisplay)
        QObject.disconnect(self.canvas, SIGNAL("renderComplete(QPainter *)"),
                           self.checkLayers)
        QObject.disconnect(self.canvas, SIGNAL("extentsChanged()"),
                           self.checkExtentsChanged)
        QObject.disconnect(self.canvas, SIGNAL("mapToolSet(QgsMapTool *)"), 
                           self.mapToolSet)
        self.using_mapnik = False
        # If the current tab is quantumnik then we need to update the extent
        # of the main map when exiting to make sure they are in sync
        if self.tabWidget.currentIndex() == 1:
            self.mapnikMapCoordChange()
        # Need to restore the main map instead of the mapnik tab
        tabWidgetSize = self.tabWidget.size()
        mapCanvasExtent = self.iface.mapCanvas().extent()
        self.iface.mapCanvas().setMinimumSize(tabWidgetSize)
        self.iface.mainWindow().setCentralWidget(self.iface.mapCanvas())
        self.iface.mapCanvas().show()
        # Set the canvas extent to the same place it was before getting
        # rid of the tabs
        self.iface.mapCanvas().setExtent(mapCanvasExtent)
        self.canvas.refresh()

        # null out some vars
        self.qCanvasPan = None
        self.qCanvasZoomIn = None
        self.qCanvasZoomOut = None
        # We have to let the main app swizzle the screen and then 
        # hammer it back to the size we want
        QTimer.singleShot(1, self.finishStopRender)

  
    def create_mapnik_map(self):
        if not self.been_warned:
            self.proj_warning()
        self.easyCanvas = sync.EasyCanvas(self,self.canvas)
        self.mapnik_map = self.easyCanvas.to_mapnik()
        if self.dock_window:
            self.view_xml(self.mapnik_map)

    @property
    def background(self):
        return sync.css_color(self.canvas.backgroundBrush().color())
        
    # Provide a hack to try and find the map coordinate status bar element
    # to take over while the mapnik canvas is in play.
    def findMapCoordsStatus(self):
        coordStatusWidget = None
        sb = self.iface.mainWindow().statusBar()
        for x in sb.children():
            # Check if we have a line edit
            if isinstance(x, QLineEdit):
                # Now check if the text does not contain a ':'
                if not ':' in x.text():
                    # we have our coord status widget
                    coordStatusWidget = x
        return coordStatusWidget

    def finishStartRendering(self):
        self.tabWidget.setMinimumSize(QSize(0, 0))
        self.canvas.refresh()

    def start_rendering(self):
        if self.from_mapfile and not self.canvas.layerCount():
            self.refresh_loaded_mapfile()
        else:
            self.from_mapfile = False
            # http://trac.osgeo.org/qgis/changeset/12926
            # TODO - if not dirty we don't need to create a new map from scratch...
            self.create_mapnik_map()
            # Need to create a tab widget to toss into the main window
            # to hold both the main canvas as well as the mapnik rendering
            mapCanvasSize = self.canvas.size()
            mapCanvasExtent = self.iface.mapCanvas().extent()
            newWidget = QTabWidget(self.iface.mainWindow())
            sizePolicy = QSizePolicy(QSizePolicy.Expanding,
                                     QSizePolicy.Preferred)
            sizePolicy.setHorizontalStretch(0)
            sizePolicy.setVerticalStretch(0)
            sizePolicy.setHeightForWidth(newWidget.sizePolicy().hasHeightForWidth())
            newWidget.setSizePolicy(sizePolicy)         
            newWidget.setSizeIncrement(QSize(0, 0))
            newWidget.setBaseSize(mapCanvasSize)
            newWidget.resize(mapCanvasSize)
            # Very important: Set the min size of the tabs to the size of the
            # original canvas.  We will then let the main app take control 
            # and then use a one shot timer to set the min size back down.  It
            # is a hack, but allows us to keep the canvas and tab size correct.
            newWidget.setMinimumSize(mapCanvasSize)
            
            # This is the new blank canvas that we will use the qpainter
            # from to draw the mapnik image over.
            self.qCanvas = QgsMapCanvas(self.iface.mainWindow())
            self.qCanvas.setCanvasColor(QColor(255,255,255))
            self.qCanvas.enableAntiAliasing(True)
            self.qCanvas.useImageToRender(False)
            self.qCanvas.show()

            # A set of map tools for the mapnik canvas
            self.qCanvasPan = QgsMapToolPan(self.qCanvas)
            self.qCanvasZoomIn = QgsMapToolZoom(self.qCanvas,False)
            self.qCanvasZoomOut = QgsMapToolZoom(self.qCanvas,True)
            self.mapToolSet(self.canvas.mapTool())

            # Add the canvas items to the tabs
            newWidget.addTab(self.canvas, "Main Map")
            newWidget.addTab(self.qCanvas, "Mapnik Rendered Map")
            self.tabWidget = newWidget
            # Add the tabs as the central widget
            self.iface.mainWindow().setCentralWidget(newWidget)
            # Need to set the extent of both canvases as we have just resized
            # things
            self.canvas.setExtent(mapCanvasExtent)
            self.qCanvas.setExtent(mapCanvasExtent)
            # Hook up to the tabs changing so we can make sure to update the 
            # rendering in a lazy way... i.e. a pan in the main canvas will 
            # not cause a redraw in the mapnik tab until the mapnik tab
            # is selected.
            self.connect(self.tabWidget,SIGNAL("currentChanged(int)"),
                         self.tabChanged)
            # Grab the maptool change signal so the mapnik canvas tool
            # can stay in sync. 
            # TODO: We need to get the in/out property for the zoom tool
            # exposed to the python bindings.  As it stands now, we can 
            # not tell what direction the tool is going when we get this
            # signal and it is a zoom tool.
            QObject.connect(self.canvas, SIGNAL("mapToolSet(QgsMapTool *)"), 
                            self.mapToolSet)
            # Catch any mouse movements over the mapnik canvas and 
            # sneek in and update the cord display
            ## This is a hack to find the status element to populate with xy
            self.mapCoords = self.findMapCoordsStatus()
            QObject.connect(self.qCanvas,
                            SIGNAL("xyCoordinates(const QgsPoint&)"),
                            self.updateCoordsDisplay)
            # Get the renderComplete signal for the qCanvas to allow us to 
            # render the mapnik image over it.
            QObject.connect(self.qCanvas, SIGNAL("renderComplete(QPainter *)"),
                            self.render_dynamic)
            # Get the renderComplete signal for the main canvas so we can tell
            # if there have been any layer changes and if we need to re-draw
            # the mapnik image.  This is mainly for when the mapnik tab is 
            # active but layer changes are happening.
            QObject.connect(self.canvas, SIGNAL("renderComplete(QPainter *)"),
                            self.checkLayers)
            QObject.connect(self.canvas, SIGNAL("extentsChanged()"),
                            self.checkExtentsChanged)
            self.using_mapnik=True
            # We use a single shot timer to let the main app resize the main
            # window with us holding a minsize we want, then we reset the
            # allowable min size after the main app has its turn.  Hack, but
            # allows for the window to be rezised with a new main widget.
            QTimer.singleShot(1, self.finishStartRendering)

    def updateCoordsDisplay(self, p):
        if self.mapCoords:
            capturePyString = "%.5f,%.5f" % (p.x(),p.y())
            capture_string = QString(capturePyString)
            self.mapCoords.setText(capture_string)

    def mapToolSet(self, tool):
        # something changed here in recent QGIS versions causing:
        # exceptions when closing QGIS because these objects are None
        if tool:
            if isinstance(tool,QgsMapToolPan):
                self.qCanvas.setMapTool(self.qCanvasPan)
            elif isinstance(tool,QgsMapToolZoom):
                # Yet another hack to find out if the tool we are using is a 
                # zoom in or out
                if tool.action().text() == QString("Zoom In"):
                    self.qCanvas.setMapTool(self.qCanvasZoomIn)
                else:
                    self.qCanvas.setMapTool(self.qCanvasZoomOut)
            else:
                self.qCanvas.setMapTool(self.qCanvasPan)

    def switch_tab_qgis(self):
        if self.tabWidget:
            self.tabWidget.setCurrentIndex(0)

    def switch_tab_mapnik(self):
        if self.tabWidget:
            self.tabWidget.setCurrentIndex(1)
        
    def tabChanged(self, index):
        if index == 0:
            self.mapnikMapCoordChange()
        else:
            self.mainMapCoordChange()

    def mainMapCoordChange(self):
        # print "coordChange"
        self.mapnik_map = self.easyCanvas.to_mapnik(self.mapnik_map)
        self.qCanvas.setExtent(self.iface.mapCanvas().extent())
        self.qCanvas.refresh()

    def mapnikMapCoordChange(self):
        # print "coordChange"
        self.canvas.setExtent(self.qCanvas.extent())
        self.canvas.refresh()

    # Here we are checking to see if we got a new extent on the main
    # canvas even though we are in the mapnik tab... in that case we have
    # done something like zoom to full extent etc.
    def checkExtentsChanged(self):
        if self.tabWidget:
            if self.tabWidget.currentIndex() == 1:
                self.mainMapCoordChange()


    # Here we are checking to see if we got a render complete on the main
    # canvas even though we are in the mapnik tab... in that case we have 
    # a new layer etc.
    def checkLayers(self, painter=None):
        if self.tabWidget:
            if self.tabWidget.currentIndex() == 1:
                # There was a change in the main canvas while we are viewing
                # the mapnik canvas (i.e. layer added/removed etc) so we 
                # need to refresh the mapnik map
                self.mapnik_map = self.easyCanvas.to_mapnik(self.mapnik_map)
                self.qCanvas.refresh()
                # We also make sure the main map canvas gets put back to the
                # current extent of the qCanvas incase the main map got changed
                # as a side effect since updates to it are lazy loaded on tab
                # change.
                self.canvas.setExtent(self.qCanvas.extent())
                # We make sure to update the XML viewer if
                # if is open
                if self.dock_window:
                    self.view_xml(self.mapnik_map)
        if self.dock_window:
            self.view_xml()
        
    def render_dynamic(self, painter):
        if self.mapnik_map:
            w = painter.device().width()
            h = painter.device().height()
            # using canvas dims leads to shift in QGIS < 1.3...
            #w = self.canvas.width()
            #h = self.canvas.height()
            try:
                self.mapnik_map.resize(w,h)
            except:
                self.mapnik_map.width = w
                self.mapnik_map.height = h
            if self.qCanvas:
                can = self.qCanvas
            else:
                can = self.canvas
            try:
                e = can.extent()
            except:
                can = self.canvas
                e = can.extent()
            bbox = mapnik.Envelope(e.xMinimum(),e.yMinimum(),
                                   e.xMaximum(),e.yMaximum())
            self.mapnik_map.zoom_to_box(bbox)
            im = mapnik.Image(w,h)
            mapnik.render(self.mapnik_map,im)
            if os.name == 'nt':
                qim = QImage()
                qim.loadFromData(QByteArray(im.tostring('png')))
                painter.drawImage(0,0,qim)
            else:
                qim = QImage(im.tostring(),w,h,QImage.Format_ARGB32)
                painter.drawImage(0,0,qim.rgbSwapped())
            can.refresh()
Beispiel #11
0
    def build(self):
        lt = self.config.get('UI', 'display_type')
        dtlut = {
            "RPI Touch": 0,
            "Small Desktop": 1,
            "Large Desktop": 2,
            "Wide Desktop": 3,
            "RPI Full Screen": 4
        }

        self.is_desktop = dtlut.get(lt, 0)

        # load the layouts for the desktop screen
        if self.is_desktop == 1:
            Builder.load_file('desktop.kv')
            Window.size = (1024, 768)

        elif self.is_desktop == 2 or self.is_desktop == 3 or self.is_desktop == 4:
            Builder.load_file('desktop_large.kv' if self.is_desktop ==
                              2 else 'desktop_wide.kv')
            if self.is_desktop != 4:
                # because rpi_egl does not like to be told the size
                s = self.config.get('UI', 'screen_size')
                if s == 'auto':
                    Window.size = (1280,
                                   1024) if self.is_desktop == 2 else (1280,
                                                                       800)
                elif 'x' in s:
                    (w, h) = s.split('x')
                    Window.size = (int(w), int(h))
                p = self.config.get('UI', 'screen_pos')
                if p != 'auto' and ',' in p:
                    (t, l) = p.split(',')
                    Window.top = int(t)
                    Window.left = int(l)
            Window.bind(on_request_close=self.window_request_close)

        else:
            self.is_desktop = 0
            # load the layouts for rpi 7" touch screen
            Builder.load_file('rpi.kv')

        self.is_cnc = self.config.getboolean('UI', 'cnc')
        self.tab_top = self.config.getboolean('UI', 'tab_top')
        self.is_webserver = self.config.getboolean('Web', 'webserver')
        self.is_show_camera = self.config.getboolean('Web', 'show_video')
        self.is_spindle_camera = self.config.getboolean(
            'General', 'is_spindle_camera')
        self.manual_tool_change = self.config.getboolean(
            'General', 'manual_tool_change')
        self.wait_on_m0 = self.config.getboolean('General', 'wait_on_m0')
        self.is_v2 = self.config.getboolean('General', 'v2')

        self.comms = Comms(App.get_running_app(),
                           self.config.getfloat('General', 'report_rate'))
        self.gcode_file = self.config.get('General', 'last_print_file')
        self.sm = ScreenManager()
        ms = MainScreen(name='main')
        self.main_window = ms.ids.main_window
        self.sm.add_widget(ms)
        self.sm.add_widget(GcodeViewerScreen(name='viewer', comms=self.comms))
        self.config_editor = ConfigEditor(name='config_editor')
        self.sm.add_widget(self.config_editor)
        self.gcode_help = GcodeHelp(name='gcode_help')
        self.sm.add_widget(self.gcode_help)
        if self.is_desktop == 0:
            self.text_editor = TextEditor(name='text_editor')
            self.sm.add_widget(self.text_editor)

        self.blank_timeout = self.config.getint('General', 'blank_timeout')
        Logger.info("SmoothieHost: screen blank set for {} seconds".format(
            self.blank_timeout))

        self.sm.bind(on_touch_down=self._on_touch)
        Clock.schedule_interval(self._every_second, 1)

        # select the file chooser to use
        # select which one we want from config
        filechooser = self.config.get('UI', 'filechooser')
        if self.is_desktop > 0:
            if filechooser != 'default':
                NativeFileChooser.type_name = filechooser
                Factory.register('filechooser', cls=NativeFileChooser)
                try:
                    f = Factory.filechooser()
                except Exception:
                    Logger.error(
                        "SmoothieHost: can't use selected file chooser: {}".
                        format(filechooser))
                    Factory.unregister('filechooser')
                    Factory.register('filechooser', cls=FileDialog)

            else:
                # use Kivy filechooser
                Factory.register('filechooser', cls=FileDialog)

            # we want to capture arrow keys
            Window.bind(on_key_down=self._on_keyboard_down)
        else:
            # use Kivy filechooser
            Factory.register('filechooser', cls=FileDialog)

        # setup for cnc or 3d printer
        if self.is_cnc:
            if self.is_desktop < 3:
                # remove Extruder panel from tabpanel and tab
                self.main_window.ids.tabs.remove_widget(
                    self.main_window.ids.tabs.extruder_tab)

        # if not CNC mode then do not show the ZABC buttons in jogrose
        if not self.is_cnc:
            self.main_window.ids.tabs.jog_rose.jogrosemain.remove_widget(
                self.main_window.ids.tabs.jog_rose.abc_panel)

        if self.is_webserver:
            self.webserver = ProgressServer()
            self.webserver.start(self, 8000)

        if self.is_show_camera:
            self.camera_url = self.config.get('Web', 'camera_url')
            self.sm.add_widget(CameraScreen(name='web cam'))
            self.main_window.tools_menu.add_widget(
                ActionButton(text='Web Cam',
                             on_press=lambda x: self._show_web_cam()))

        if self.is_spindle_camera:
            if self.is_desktop in [0, 4]:
                try:
                    self.sm.add_widget(SpindleCamera(name='spindle camera'))
                except Exception as err:
                    self.main_window.display(
                        'ERROR: failed to load spindle camera. Check logs')
                    Logger.error(
                        'Main: spindle camera exception: {}'.format(err))

            self.main_window.tools_menu.add_widget(
                ActionButton(text='Spindle Cam',
                             on_press=lambda x: self._show_spindle_cam()))

        # load any modules specified in config
        self._load_modules()

        if self.blank_timeout > 0:
            # unblank if blanked
            self.unblank_screen()

        return self.sm
Beispiel #12
0
        # Za povratke vaše
        #
        # O, igračke drage
        # Iz djetinjstva mog"""
        #         "This is the longest line ever in the world here ever longest really "
        #         "longest too long for ordinary people for ordinary world too too long for anything in reality for real cmon "
        #         "too long really too too too long\n"
        #         "This is the longest line ever in the world here ever longest really "
        #         "longest too long for ordinary people for ordinary world too too long for anything in reality for real cmon "
        #         "too long really too too too long\n"
        #         "This is the longest line ever in the world here ever longest really "
        #         "longest too long for ordinary people for ordinary world too too long for anything in reality for real cmon "
        #         "too long really too too too long\n"
        "The first line.\n"
        "In between the two...\n"
        "The last line.")

    root = Tk()
    tedi = TextEditor(model)
    root.geometry("555x800+300+300")
    root.mainloop()
"""
"Vidimo da osnovni razred omogućava da grafički podsustav samostalno poziva naš kod za crtanje kad god se za to 
javi potreba, iako je oblikovan i izveden davno prije naše grafičke komponente. Koji oblikovni obrazac to omogućava?" 
--> TODO 

"Vidimo također da naša grafička komponenta preko osnovnog razreda može dobiti informacije o pritisnutim tipkama 
bez potrebe za čekanjem u radnoj petlji. Koji oblikovni obrazac to omogućava?"
--> Observer
"""
Beispiel #13
0
    def __init__(self):
        """Initialize Tab with layout and behavior."""
        super(Tab, self).__init__()

        # regex pattern for SQL query block comments
        self.block_comment_re = re.compile(
            r'(^)?[^\S\n]*/(?:\*(.*?)\*/[^\S\n]*|/[^\n]*)($)?',
            re.DOTALL | re.MULTILINE)

        main_layout = QVBoxLayout(self)

        # define gdb props
        self.gdb = None
        self.gdb_items = None
        self.gdb_columns_names = None
        self.gdb_schemas = None

        # connected geodatabase path toolbar
        self.connected_gdb_path_label = QLabel('')
        self.connected_gdb_path_label.setTextInteractionFlags(
            Qt.TextSelectableByMouse)
        self.connected_gdb_path_label.setToolTip(
            'Connected geodatabase that queries will be run against')
        self.connected_gdb_path_label.setText(not_connected_to_gdb_message)

        self.browse_to_gdb = QPushButton('Browse')
        self.browse_to_gdb.setShortcut(QKeySequence('Ctrl+B'))
        self.browse_to_gdb.clicked.connect(
            lambda evt, arg=True: self.connect_to_geodatabase(
                evt, triggered_with_browse=True))

        self.gdb_sql_dialect_combobox = QComboBox()
        for dialect in sql_dialects_names:
            self.gdb_sql_dialect_combobox.addItem(dialect)

        self.gdb_browse_toolbar = QToolBar()
        self.gdb_browse_toolbar.setMaximumHeight(50)
        self.gdb_browse_toolbar.addWidget(self.browse_to_gdb)
        self.gdb_browse_toolbar.addWidget(self.connected_gdb_path_label)
        self.gdb_browse_toolbar.addSeparator()
        self.gdb_browse_toolbar.addWidget(self.gdb_sql_dialect_combobox)

        # table with results
        self.table = ResultTable()

        # execute SQL query
        self.execute = QAction('Execute', self)
        self.execute.setShortcuts(
            [QKeySequence('F5'),
             QKeySequence('Ctrl+Return')])
        self.execute.triggered.connect(self.run_query)
        self.addAction(self.execute)

        # enter a SQL query
        self.query = TextEditor()
        self.query.setPlainText('')
        font = self.query.font()
        font.setFamily('Consolas')
        font.setStyleHint(QFont.Monospace)

        # TODO: add line numbers to the text editor
        font.setPointSize(14)
        self.query.setFont(font)
        self.query.setTabStopWidth(20)
        self.highlighter = Highlighter(self.query.document())

        # TODO select block of text - Ctrl+/ and they become comments
        self.completer = Completer()
        self.query.set_completer(self.completer.completer)

        # errors panel to show if query fails to execute properly
        self.errors_panel = QPlainTextEdit()
        font = self.query.font()
        font.setPointSize(12)
        self.errors_panel.setStyleSheet('color:red')
        self.errors_panel.setFont(font)
        self.errors_panel.hide()

        # splitter between the toolbar, query window, and the result set table
        splitter = QSplitter(Qt.Vertical)
        splitter.addWidget(self.gdb_browse_toolbar)
        splitter.addWidget(self.query)
        splitter.addWidget(self.table)
        splitter.addWidget(self.errors_panel)

        # add the settings after the widget have been added
        splitter.setCollapsible(0, True)
        splitter.setCollapsible(1, False)
        splitter.setCollapsible(2, False)
        splitter.setCollapsible(3, False)
        splitter.setStretchFactor(0, 3)
        splitter.setStretchFactor(1, 7)
        splitter.setSizes((100, 200, 300))
        self.table.hide()

        # TOC
        self.toc = QTreeWidget()
        self.toc.setHeaderHidden(True)

        # second splitter between the TOC to the left and the query/table to the
        # right
        toc_splitter = QSplitter(Qt.Horizontal)
        toc_splitter.addWidget(self.toc)
        toc_splitter.addWidget(splitter)
        toc_splitter.setCollapsible(0, True)
        toc_splitter.setSizes((200, 800))  # set the TOC vs data panel

        main_layout.addWidget(toc_splitter)

        margins = QMargins()
        margins.setBottom(10)
        margins.setLeft(10)
        margins.setRight(10)
        margins.setTop(10)
        main_layout.setContentsMargins(margins)

        self.setLayout(main_layout)
        QApplication.setStyle(QStyleFactory.create('Cleanlooks'))
        self.show()

        return
Beispiel #14
0
class Tab(QWidget):
    """Tab in the QTableWidget where user executes query and sees the result."""

    # ----------------------------------------------------------------------
    def __init__(self):
        """Initialize Tab with layout and behavior."""
        super(Tab, self).__init__()

        # regex pattern for SQL query block comments
        self.block_comment_re = re.compile(
            r'(^)?[^\S\n]*/(?:\*(.*?)\*/[^\S\n]*|/[^\n]*)($)?',
            re.DOTALL | re.MULTILINE)

        main_layout = QVBoxLayout(self)

        # define gdb props
        self.gdb = None
        self.gdb_items = None
        self.gdb_columns_names = None
        self.gdb_schemas = None

        # connected geodatabase path toolbar
        self.connected_gdb_path_label = QLabel('')
        self.connected_gdb_path_label.setTextInteractionFlags(
            Qt.TextSelectableByMouse)
        self.connected_gdb_path_label.setToolTip(
            'Connected geodatabase that queries will be run against')
        self.connected_gdb_path_label.setText(not_connected_to_gdb_message)

        self.browse_to_gdb = QPushButton('Browse')
        self.browse_to_gdb.setShortcut(QKeySequence('Ctrl+B'))
        self.browse_to_gdb.clicked.connect(
            lambda evt, arg=True: self.connect_to_geodatabase(
                evt, triggered_with_browse=True))

        self.gdb_sql_dialect_combobox = QComboBox()
        for dialect in sql_dialects_names:
            self.gdb_sql_dialect_combobox.addItem(dialect)

        self.gdb_browse_toolbar = QToolBar()
        self.gdb_browse_toolbar.setMaximumHeight(50)
        self.gdb_browse_toolbar.addWidget(self.browse_to_gdb)
        self.gdb_browse_toolbar.addWidget(self.connected_gdb_path_label)
        self.gdb_browse_toolbar.addSeparator()
        self.gdb_browse_toolbar.addWidget(self.gdb_sql_dialect_combobox)

        # table with results
        self.table = ResultTable()

        # execute SQL query
        self.execute = QAction('Execute', self)
        self.execute.setShortcuts(
            [QKeySequence('F5'),
             QKeySequence('Ctrl+Return')])
        self.execute.triggered.connect(self.run_query)
        self.addAction(self.execute)

        # enter a SQL query
        self.query = TextEditor()
        self.query.setPlainText('')
        font = self.query.font()
        font.setFamily('Consolas')
        font.setStyleHint(QFont.Monospace)

        # TODO: add line numbers to the text editor
        font.setPointSize(14)
        self.query.setFont(font)
        self.query.setTabStopWidth(20)
        self.highlighter = Highlighter(self.query.document())

        # TODO select block of text - Ctrl+/ and they become comments
        self.completer = Completer()
        self.query.set_completer(self.completer.completer)

        # errors panel to show if query fails to execute properly
        self.errors_panel = QPlainTextEdit()
        font = self.query.font()
        font.setPointSize(12)
        self.errors_panel.setStyleSheet('color:red')
        self.errors_panel.setFont(font)
        self.errors_panel.hide()

        # splitter between the toolbar, query window, and the result set table
        splitter = QSplitter(Qt.Vertical)
        splitter.addWidget(self.gdb_browse_toolbar)
        splitter.addWidget(self.query)
        splitter.addWidget(self.table)
        splitter.addWidget(self.errors_panel)

        # add the settings after the widget have been added
        splitter.setCollapsible(0, True)
        splitter.setCollapsible(1, False)
        splitter.setCollapsible(2, False)
        splitter.setCollapsible(3, False)
        splitter.setStretchFactor(0, 3)
        splitter.setStretchFactor(1, 7)
        splitter.setSizes((100, 200, 300))
        self.table.hide()

        # TOC
        self.toc = QTreeWidget()
        self.toc.setHeaderHidden(True)

        # second splitter between the TOC to the left and the query/table to the
        # right
        toc_splitter = QSplitter(Qt.Horizontal)
        toc_splitter.addWidget(self.toc)
        toc_splitter.addWidget(splitter)
        toc_splitter.setCollapsible(0, True)
        toc_splitter.setSizes((200, 800))  # set the TOC vs data panel

        main_layout.addWidget(toc_splitter)

        margins = QMargins()
        margins.setBottom(10)
        margins.setLeft(10)
        margins.setRight(10)
        margins.setTop(10)
        main_layout.setContentsMargins(margins)

        self.setLayout(main_layout)
        QApplication.setStyle(QStyleFactory.create('Cleanlooks'))
        self.show()

        return

    # ----------------------------------------------------------------------
    def connect_to_geodatabase(self, evt, triggered_with_browse=True):
        """Connect to geodatabase by letting user browse to a gdb folder."""
        if triggered_with_browse:
            gdb_connect_dialog = QFileDialog(self)
            gdb_connect_dialog.setFileMode(QFileDialog.Directory)
            gdb_path = gdb_connect_dialog.getExistingDirectory()

            # TODO: add a filter to show only .gdb folders?
            # https://stackoverflow.com/questions/4893122/filtering-in-qfiledialog
            if gdb_path and gdb_path.endswith('.gdb'):
                self.gdb = Geodatabase(gdb_path)
                if self.gdb.is_valid():
                    self.connected_gdb_path_label.setText(self.gdb.path)
                    self._set_gdb_items_highlight()
                    self._set_gdb_items_complete()
                    self._fill_toc()
                else:
                    msg = QMessageBox()
                    msg.setText('This is not a valid file geodatabase')
                    msg.setWindowTitle('Validation error')
                    msg.setStandardButtons(QMessageBox.Ok)
                    msg.exec_()
        else:
            if self.gdb.is_valid():
                self._set_gdb_items_highlight()
                self._set_gdb_items_complete()

        return

    # ----------------------------------------------------------------------
    def wheelEvent(self, event):  # noqa: N802
        """Override built-in method to handle mouse wheel scrolling.

        Necessary to do when the tab is focused.
        """
        modifiers = QApplication.keyboardModifiers()
        if modifiers == Qt.ControlModifier:
            if event.angleDelta().y() > 0:  # scroll forward
                self.query.zoomIn(1)
            else:
                self.query.zoomOut(1)
        return

    # ----------------------------------------------------------------------
    def run_query(self):
        """Run SQL query and draw the record set and call table drawing."""
        if not self.gdb:
            self.print_sql_execute_errors(not_connected_to_gdb_message)
            return
        try:
            if not self.gdb.is_valid():
                return

            # use the text of what user selected, if none -> need to run the
            # whole query
            part_sql_query = self.query.textCursor().selection().toPlainText()

            if part_sql_query:
                sql_query = part_sql_query
            else:
                sql_query = self.query.toPlainText()

            if sql_query:
                # removing block comments and single line comments
                sql_query = self.block_comment_re.sub(
                    self._strip_block_comments, sql_query)
                sql_query = self._strip_single_comments(sql_query)
            else:
                return

            # TODO: add threading to allow user to cancel a long running query
            QApplication.setOverrideCursor(Qt.WaitCursor)
            start_time = time.time()
            self.gdb.open_connection()
            res, errors = self.gdb.execute_sql(
                sql_query, self.gdb_sql_dialect_combobox.currentText())
            end_time = time.time()
            if errors:
                self.print_sql_execute_errors(errors)

            if res:
                self.table.show()
                self.errors_panel.hide()
                self.draw_result_table(res)
                msg = 'Executed in {exec_time:.1f} secs | {rows} rows'.format(
                    exec_time=end_time - start_time,
                    rows=self.table.table_data.number_layer_rows)
                self.update_app_status_bar(msg)

        except Exception as err:
            print(err)
        finally:
            QApplication.restoreOverrideCursor()
        return

    # ----------------------------------------------------------------------
    def result_should_include_geometry(self):
        """Get the setting defining whether to include the geometry column."""
        try:
            return self.parentWidget().parentWidget().parentWidget(
            ).do_include_geometry.isChecked()
        except Exception:
            return True

    # ----------------------------------------------------------------------
    def update_app_status_bar(self, message):
        """Update app status bar with the execution result details."""
        try:
            self.parentWidget().parentWidget().parentWidget().statusBar(
            ).showMessage(message)
        except Exception:
            pass
        return

    # ----------------------------------------------------------------------
    def draw_result_table(self, res):
        """Draw table with the record set received from the geodatabase."""
        geom_col_name = res.GetGeometryColumn(
        )  # shape col was in the sql query
        self.geometry_isin_query = bool(geom_col_name)

        self.table.draw_result(res,
                               show_shapes=bool(
                                   self.result_should_include_geometry()))
        self.table.view.resizeColumnsToContents()
        return

    # ----------------------------------------------------------------------
    def print_sql_execute_errors(self, err):
        """Print to a special panel errors that occurred during execution."""
        self.table.hide()
        self.errors_panel.show()
        self.errors_panel.setPlainText(err)
        return

    # ----------------------------------------------------------------------
    def _set_gdb_items_highlight(self):
        """Set completer and highlight properties for geodatabase items."""
        self.gdb_items = self.gdb.get_items()
        self.highlighter.set_highlight_rules_gdb_items(self.gdb_items, 'Table')

        self.gdb_schemas = self.gdb.get_schemas()
        self.gdb_columns_names = sorted(list(
            set(
                itertools.chain.from_iterable(
                    [i.keys() for i in self.gdb_schemas.values()]))),
                                        key=lambda x: x.lower())

    # ----------------------------------------------------------------------
    def _set_gdb_items_complete(self):
        """Update completer rules to include geodatabase items."""
        self.completer.update_completer_string_list(self.gdb_items +
                                                    self.gdb_columns_names)
        self.highlighter.set_highlight_rules_gdb_items(self.gdb_columns_names,
                                                       'Column')
        return

    # ----------------------------------------------------------------------
    def _fill_toc(self):
        """Fill TOC with geodatabase datasets and columns."""
        self.toc.clear()
        if not self.gdb_items:
            return

        for tbl_name in sorted(self.gdb_items, key=lambda i: i.lower()):
            if tbl_name.islower() or tbl_name.isupper():
                item = QTreeWidgetItem([tbl_name.title()])
            else:
                item = QTreeWidgetItem([tbl_name])
            font = QFont()
            font.setBold(True)
            item.setFont(0, font)

            for col_name, col_type in sorted(
                    self.gdb_schemas[tbl_name].items()):
                if col_name.islower() or col_name.isupper():
                    col_name = col_name.title()

                item_child = QTreeWidgetItem(
                    ['{0} ({1})'.format(col_name, col_type)])
                item.addChild(item_child)
            self.toc.addTopLevelItem(item)
        return

    # ----------------------------------------------------------------------
    def _do_toc_hide_show(self):
        """Hide TOC with tables and columns."""
        if self.toc.isVisible():
            self.toc.setVisible(False)
        else:
            self.toc.setVisible(True)
        return

    # ----------------------------------------------------------------------
    def _strip_block_comments(self, sql_query):
        """Strip the block comments in SQL query."""
        start, mid, end = sql_query.group(1, 2, 3)
        if mid is None:
            # this is a single-line comment
            return ''
        elif start is not None or end is not None:
            # this is a multi-line comment at start/end of a line
            return ''
        elif '\n' in mid:
            # this is a multi-line comment with line break
            return '\n'
        else:
            # this is a multi-line comment without line break
            return ' '

    # ----------------------------------------------------------------------
    def _strip_single_comments(self, sql_query):
        """Strip the single line comments in SQL query."""
        clean_query = []
        for line in sql_query.rstrip().split('\n'):
            clean_line = line.split('--')[0]
            if clean_line:
                clean_query.append(clean_line)
        return ' '.join([line for line in clean_query])
Beispiel #15
0
class TextEditorApp:
    """
    TextEditorApp class that works as "Caretaker" that actually uses Memento
    Pattern.
    This class is responsible for keeping a collection of "Memento", but never
    examines or operates on the contents of a "Memento".
    """
    __slots__ = ['_text_editor', '_memento_list', '_prev_state_ptr']

    def __init__(self):
        """
        Default constructor.
        """
        self._text_editor = TextEditor()
        self._memento_list = []
        self._prev_state_ptr = -1

    def write(self, text: str) -> None:
        """
        Writes the given text to the text editor.
        :param text: str
        :return: None
        """
        self._text_editor.text = text
        print(f"After writing '{text}': {self._text_editor}")

    def save(self) -> None:
        """
        Saves the current state of the text editor.
        :return: None
        """
        self._add_memento(self._text_editor.create_memento())
        self._prev_state_ptr = len(self._memento_list) - 1

    def _add_memento(self, memo: Memento) -> None:
        """
        Private helper method to store the given memento.
        :param memo: Memento
        :return: None
        """
        self._memento_list.append(memo)

    def undo(self) -> None:
        """
        Undo the most recent change in the text editor.
        :return: None
        """
        print('Undo operation...')
        if self._text_editor.text !=\
                self._memento_list[self._prev_state_ptr].text:
            last_saved_state = self._memento_list[self._prev_state_ptr]
            self._text_editor.restore(last_saved_state)
            print(self._text_editor)
        elif self._prev_state_ptr <= 0:
            print('No more undo operation available!')
        else:
            self._prev_state_ptr -= 1
            prev_state = self._get_memento(self._prev_state_ptr)
            self._text_editor.restore(prev_state)
            print(self._text_editor)

    def _get_memento(self, idx: int) -> Memento:
        """
        Private helper method to get the memento at the given index.
        :param idx: int
        :return: Memento
        """
        return self._memento_list[idx]

    def redo(self) -> None:
        """
        Redoes the most recent change in the text editor.
        :return: None
        """
        print('Redo operation...')
        if self._prev_state_ptr >= len(self._memento_list) - 1:
            print('No more redo operation available!')
            return
        self._prev_state_ptr += 1
        next_state = self._get_memento(self._prev_state_ptr)
        self._text_editor.restore(next_state)
        print(self._text_editor)
Beispiel #16
0
class Quantumnik(QObject):
    def __init__(self, iface):
        QObject.__init__(self)
        self.iface = iface
        self.canvas = iface.mapCanvas()

        # Fake canvas to use in tab to overlay the quantumnik layer
        self.qCanvas = None
        self.qCanvasPan = None
        self.qCanvasZoomIn = None
        self.qCanvasZoomOut = None
        self.tabWidget = None

        self.mapnik_map = None
        self.using_mapnik = False
        self.from_mapfile = False
        self.loaded_mapfile = None
        self.been_warned = False
        self.last_image_path = None
        self.dock_window = None
        self.keyAction = None
        self.keyAction2 = None
        self.keyAction3 = None

    def initGui(self):
        self.action = QAction(QIcon(":/mapnikglobe.png"),
                              QString("Create Mapnik Canvas"),
                              self.iface.mainWindow())
        self.action.setWhatsThis("Create Mapnik Canvas")
        self.action.setStatusTip("%s: render with Mapnik" % NAME)
        QObject.connect(self.action, SIGNAL("triggered()"), self.toggle)

        self.action4 = QAction(QString("View live xml"),
                               self.iface.mainWindow())
        QObject.connect(self.action4, SIGNAL("triggered()"), self.view_xml)

        self.action3 = QAction(QString("Export Mapnik xml"),
                               self.iface.mainWindow())
        QObject.connect(self.action3, SIGNAL("triggered()"), self.save_xml)

        self.action5 = QAction(QString("Load Mapnik xml"),
                               self.iface.mainWindow())
        QObject.connect(self.action5, SIGNAL("triggered()"), self.load_xml)

        self.action6 = QAction(QString("Load Cascadenik mml"),
                               self.iface.mainWindow())
        QObject.connect(self.action6, SIGNAL("triggered()"), self.load_mml)

        self.action7 = QAction(QString("Export Map Graphics"),
                               self.iface.mainWindow())
        QObject.connect(self.action7, SIGNAL("triggered()"),
                        self.export_image_gui)

        self.helpaction = QAction(QIcon(":/mapnikhelp.png"), "About",
                                  self.iface.mainWindow())
        self.helpaction.setWhatsThis("%s Help" % NAME)
        QObject.connect(self.helpaction, SIGNAL("triggered()"), self.helprun)

        self.iface.addToolBarIcon(self.action)

        self.iface.addPluginToMenu("&%s" % NAME, self.action)
        self.iface.addPluginToMenu("&%s" % NAME, self.helpaction)
        self.iface.addPluginToMenu("&%s" % NAME, self.action3)
        self.iface.addPluginToMenu("&%s" % NAME, self.action4)
        self.iface.addPluginToMenu("&%s" % NAME, self.action5)
        self.iface.addPluginToMenu("&%s" % NAME, self.action6)
        self.iface.addPluginToMenu("&%s" % NAME, self.action7)

        # > QGIS 1.2
        if hasattr(self.iface, 'registerMainWindowAction'):
            self.keyAction2 = QAction(QString("Switch to QGIS"),
                                      self.iface.mainWindow())
            self.iface.registerMainWindowAction(self.keyAction2, "Ctrl+[")
            self.iface.addPluginToMenu("&%s" % NAME, self.keyAction2)
            QObject.connect(self.keyAction2, SIGNAL("triggered()"),
                            self.switch_tab_qgis)

            self.keyAction3 = QAction(QString("Switch to Mapnik"),
                                      self.iface.mainWindow())
            self.iface.registerMainWindowAction(self.keyAction3, "Ctrl+]")
            self.iface.addPluginToMenu("&%s" % NAME, self.keyAction3)
            QObject.connect(self.keyAction3, SIGNAL("triggered()"),
                            self.switch_tab_mapnik)

    def unload(self):
        self.iface.removePluginMenu("&%s" % NAME, self.action)
        self.iface.removePluginMenu("&%s" % NAME, self.helpaction)
        self.iface.removePluginMenu("&%s" % NAME, self.action3)
        self.iface.removePluginMenu("&%s" % NAME, self.action4)
        self.iface.removePluginMenu("&%s" % NAME, self.action5)
        self.iface.removePluginMenu("&%s" % NAME, self.action6)
        self.iface.removePluginMenu("&%s" % NAME, self.action7)
        self.iface.removeToolBarIcon(self.action)
        if self.keyAction:
            self.iface.unregisterMainWindowAction(self.keyAction)
        if self.keyAction2:
            self.iface.unregisterMainWindowAction(self.keyAction2)
        if self.keyAction3:
            self.iface.unregisterMainWindowAction(self.keyAction3)

    def export_image_gui(self):
        flags = Qt.WindowTitleHint | Qt.WindowSystemMenuHint | Qt.WindowStaysOnTopHint
        export = ImageExport(self, flags)
        export.show()

    def view_xml(self, m=None):
        if not self.dock_window:
            self.dock_window = TextEditor(self)
            self.iface.mainWindow().addDockWidget(Qt.BottomDockWidgetArea,
                                                  self.dock_window)
            if not self.using_mapnik:
                # http://trac.osgeo.org/qgis/changeset/12955 - render starting signal
                QObject.connect(self.canvas,
                                SIGNAL("renderComplete(QPainter *)"),
                                self.checkLayers)

        self.dock_window.show()
        if self.loaded_mapfile:
            # if we have loaded a map xml or mml
            # so lets just display the active file
            xml = open(self.loaded_mapfile, 'rb').read()
        else:
            if not m:
                # regenerate from qgis objects
                e_c = sync.EasyCanvas(self, self.canvas)
                m = e_c.to_mapnik()
            if hasattr(mapnik, 'save_map_to_string'):
                xml = mapnik.save_map_to_string(m)
            else:
                (handle, mapfile) = tempfile.mkstemp('.xml', 'quantumnik-map-')
                os.close(handle)
                mapnik.save_map(m, str(mapfile))
                xml = open(mapfile, 'rb').read()
        e = self.canvas.extent()
        bbox = '%s %s %s %s' % (e.xMinimum(), e.yMinimum(), e.xMaximum(),
                                e.yMaximum())
        cmd = '\n<!-- nik2img.py mapnik.xml out.png -d %s %s -e %s -->\n' % (
            self.canvas.width(), self.canvas.height(), bbox)
        try:
            if self.mapnik_map:
                cmd += '<!-- <MinScaleDenominator>%s</MinScaleDenominator> -->\n' % (
                    self.mapnik_map.scale_denominator())
        except:
            pass

        code = xml + cmd
        if HIGHLIGHTING:
            highlighted = highlight(
                code, XmlLexer(),
                HtmlFormatter(linenos=False, nowrap=False, full=True))
            self.dock_window.textEdit.setHtml(highlighted)
        else:
            self.dock_window.textEdit.setText(xml + cmd)

    def helprun(self):
        infoString = QString(
            "Written by Dane Springmeyer\nhttps://github.com/springmeyer/quantumnik"
        )
        QMessageBox.information(self.iface.mainWindow(), "About %s" % NAME,
                                infoString)

    def toggle(self):
        if self.using_mapnik:
            self.stop_rendering()
        else:
            self.start_rendering()

    def proj_warning(self):
        self.been_warned = True
        ren = self.canvas.mapRenderer()
        if not ren.hasCrsTransformEnabled() and self.canvas.layerCount() > 1:
            if hasattr(self.canvas.layer(0), 'crs'):
                if not self.canvas.layer(
                        0).crs().toProj4() == ren.destinationCrs().toProj4():
                    QMessageBox.information(
                        self.iface.mainWindow(), "Warning",
                        "The projection of the map and the first layer do not match. Mapnik may not render the layer(s) correctly.\n\nYou likely need to either enable 'On-the-fly' CRS transformation or set the Map projection in your Project Properties to the projection of your layer(s)."
                    )
            else:
                if not self.canvas.layer(
                        0).srs().toProj4() == ren.destinationSrs().toProj4():
                    QMessageBox.information(
                        self.iface.mainWindow(), "Warning",
                        "The projection of the map and the first layer do not match. Mapnik may not render the layer(s) correctly.\n\nYou likely need to either enable 'On-the-fly' CRS transformation or set the Map projection in your Project Properties to the projection of your layer(s)."
                    )

    def save_xml(self):
        # need to expose as an option!
        relative_paths = True

        mapfile = QFileDialog.getSaveFileName(None, "Save file dialog",
                                              'mapnik.xml', "Mapfile (*.xml)")
        if mapfile:
            e_c = sync.EasyCanvas(self, self.canvas)
            mapfile_ = str(mapfile)
            base_path = os.path.dirname(mapfile_)
            e_c.base_path = base_path
            m = e_c.to_mapnik()
            mapnik.save_map(m, mapfile_)

            if relative_paths:
                relativism.fix_paths(mapfile_, base_path)

    def make_bundle(self):
        pass

    # todo: accept directory name
    # move mapfile and all file based datasources
    # into that folder and stash some docs inside
    # provide option to zip and upload to url on the fly

    def set_canvas_from_mapnik(self):
        # set up keyboard shortcut
        # > QGIS 1.2
        if hasattr(self.iface, 'registerMainWindowAction'):
            if not self.keyAction:
                # TODO - hotkey does not work on linux....
                self.keyAction = QAction(QString("Refresh " + NAME),
                                         self.iface.mainWindow())
                self.iface.registerMainWindowAction(self.keyAction, "Ctrl+r")
                self.iface.addPluginToMenu("&%s" % NAME, self.keyAction)
                QObject.connect(self.keyAction, SIGNAL("triggered()"),
                                self.toggle)

        self.mapnik_map.zoom_all()
        e = self.mapnik_map.envelope()
        crs = QgsCoordinateReferenceSystem()
        srs = self.mapnik_map.srs
        if srs == '+init=epsg:900913':
            # until we can look it up in srs.db...
            merc = "+init=EPSG:900913"
            crs.createFromProj4(QString(merc))
        elif 'init' in srs:
            # TODO - quick hack, needs regex and fallbacks
            epsg = srs.split(':')[1]
            crs.createFromEpsg(int(epsg))
        else:
            if srs == '+proj=latlong +datum=WGS84':
                # expand the Mapnik srs a bit
                # http://trac.mapnik.org/ticket/333
                srs = '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs'
            crs.createFromProj4(QString(srs))
        if hasattr(self.canvas.mapRenderer(), 'setDestinationCrs'):
            self.canvas.mapRenderer().setDestinationCrs(crs)
        else:
            self.canvas.mapRenderer().setDestinationSrs(crs)
        if not crs.isValid():
            QMessageBox.information(self.iface.mainWindow(), "Warning",
                                    "Projection not understood")
            return
        QObject.connect(self.canvas, SIGNAL("renderComplete(QPainter *)"),
                        self.render_dynamic)
        self.canvas.setExtent(QgsRectangle(e.minx, e.miny, e.maxx, e.maxy))
        self.canvas.refresh()

    def set_mapnik_to_canvas(self):
        QObject.connect(self.canvas, SIGNAL("renderComplete(QPainter *)"),
                        self.render_dynamic)
        self.canvas.refresh()

    def refresh_loaded_mapfile(self):
        if self.mapfile_format == 'Cascadenik mml':
            self.load_mml(refresh=True)
        else:
            self.load_xml(refresh=True)

    def load_mml(self, refresh=False):
        self.from_mapfile = True
        self.mapfile_format = 'Cascadenik mml'
        if self.loaded_mapfile and refresh:
            mapfile = self.loaded_mapfile
        else:
            mapfile = QFileDialog.getOpenFileName(None, "Open file dialog", '',
                                                  "Cascadenik MML (*.mml)")
        if mapfile:
            self.mapnik_map = mapnik.Map(1, 1)
            import cascadenik
            if hasattr(cascadenik, 'VERSION'):
                major = int(cascadenik.VERSION.split('.')[0])
                if major < 1:
                    from cascadenik import compile
                    compiled = '%s_compiled.xml' % os.path.splitext(
                        str(mapfile))[0]
                    open(compiled, 'w').write(compile(str(mapfile)))
                    mapnik.load_map(self.mapnik_map, compiled)
                elif major == 1:
                    output_dir = os.path.dirname(str(mapfile))
                    cascadenik.load_map(self.mapnik_map,
                                        str(mapfile),
                                        output_dir,
                                        verbose=False)
                elif major > 1:
                    raise NotImplementedError(
                        'This nik2img version does not yet support Cascadenik > 1.x, please upgrade nik2img to the latest release'
                    )
            else:
                from cascadenik import compile
                compiled = '%s_compiled.xml' % os.path.splitext(
                    str(mapfile))[0]
                #if os.path.exits(compiled):
                #pass
                open(compiled, 'w').write(compile(str(mapfile)))
                mapnik.load_map(self.mapnik_map, compiled)

            if self.loaded_mapfile and refresh:
                self.set_mapnik_to_canvas()
            else:
                self.set_canvas_from_mapnik()
            self.loaded_mapfile = str(mapfile)

    def load_xml(self, refresh=False):
        # TODO - consider putting into its own layer:
        # https://trac.osgeo.org/qgis/ticket/2392#comment:4
        self.from_mapfile = True
        self.mapfile_format = 'xml mapfile'
        if self.loaded_mapfile and refresh:
            mapfile = self.loaded_mapfile
        else:
            mapfile = QFileDialog.getOpenFileName(None, "Open file dialog", '',
                                                  "XML Mapfile (*.xml)")
        if mapfile:
            self.mapnik_map = mapnik.Map(1, 1)
            mapnik.load_map(self.mapnik_map, str(mapfile))
            if self.loaded_mapfile and refresh:
                self.set_mapnik_to_canvas()
            else:
                self.set_canvas_from_mapnik()
            self.loaded_mapfile = str(mapfile)

    def finishStopRender(self):
        self.iface.mapCanvas().setMinimumSize(QSize(0, 0))

    def stop_rendering(self):
        # Disconnect all the signals as we disable the tool
        QObject.disconnect(self.qCanvas, SIGNAL("renderComplete(QPainter *)"),
                           self.render_dynamic)
        QObject.disconnect(self.qCanvas,
                           SIGNAL("xyCoordinates(const QgsPoint&)"),
                           self.updateCoordsDisplay)
        QObject.disconnect(self.canvas, SIGNAL("renderComplete(QPainter *)"),
                           self.checkLayers)
        QObject.disconnect(self.canvas, SIGNAL("extentsChanged()"),
                           self.checkExtentsChanged)
        QObject.disconnect(self.canvas, SIGNAL("mapToolSet(QgsMapTool *)"),
                           self.mapToolSet)
        self.using_mapnik = False
        # If the current tab is quantumnik then we need to update the extent
        # of the main map when exiting to make sure they are in sync
        if self.tabWidget.currentIndex() == 1:
            self.mapnikMapCoordChange()
        # Need to restore the main map instead of the mapnik tab
        tabWidgetSize = self.tabWidget.size()
        mapCanvasExtent = self.iface.mapCanvas().extent()
        self.iface.mapCanvas().setMinimumSize(tabWidgetSize)
        self.iface.mainWindow().setCentralWidget(self.iface.mapCanvas())
        self.iface.mapCanvas().show()
        # Set the canvas extent to the same place it was before getting
        # rid of the tabs
        self.iface.mapCanvas().setExtent(mapCanvasExtent)
        self.canvas.refresh()

        # null out some vars
        self.qCanvasPan = None
        self.qCanvasZoomIn = None
        self.qCanvasZoomOut = None
        # We have to let the main app swizzle the screen and then
        # hammer it back to the size we want
        QTimer.singleShot(1, self.finishStopRender)

    def create_mapnik_map(self):
        if not self.been_warned:
            self.proj_warning()
        self.easyCanvas = sync.EasyCanvas(self, self.canvas)
        self.mapnik_map = self.easyCanvas.to_mapnik()
        if self.dock_window:
            self.view_xml(self.mapnik_map)

    @property
    def background(self):
        return sync.css_color(self.canvas.backgroundBrush().color())

    # Provide a hack to try and find the map coordinate status bar element
    # to take over while the mapnik canvas is in play.
    def findMapCoordsStatus(self):
        coordStatusWidget = None
        sb = self.iface.mainWindow().statusBar()
        for x in sb.children():
            # Check if we have a line edit
            if isinstance(x, QLineEdit):
                # Now check if the text does not contain a ':'
                if not ':' in x.text():
                    # we have our coord status widget
                    coordStatusWidget = x
        return coordStatusWidget

    def finishStartRendering(self):
        self.tabWidget.setMinimumSize(QSize(0, 0))
        self.canvas.refresh()

    def start_rendering(self):
        if self.from_mapfile and not self.canvas.layerCount():
            self.refresh_loaded_mapfile()
        else:
            self.from_mapfile = False
            # http://trac.osgeo.org/qgis/changeset/12926
            # TODO - if not dirty we don't need to create a new map from scratch...
            self.create_mapnik_map()
            # Need to create a tab widget to toss into the main window
            # to hold both the main canvas as well as the mapnik rendering
            mapCanvasSize = self.canvas.size()
            mapCanvasExtent = self.iface.mapCanvas().extent()
            newWidget = QTabWidget(self.iface.mainWindow())
            sizePolicy = QSizePolicy(QSizePolicy.Expanding,
                                     QSizePolicy.Preferred)
            sizePolicy.setHorizontalStretch(0)
            sizePolicy.setVerticalStretch(0)
            sizePolicy.setHeightForWidth(
                newWidget.sizePolicy().hasHeightForWidth())
            newWidget.setSizePolicy(sizePolicy)
            newWidget.setSizeIncrement(QSize(0, 0))
            newWidget.setBaseSize(mapCanvasSize)
            newWidget.resize(mapCanvasSize)
            # Very important: Set the min size of the tabs to the size of the
            # original canvas.  We will then let the main app take control
            # and then use a one shot timer to set the min size back down.  It
            # is a hack, but allows us to keep the canvas and tab size correct.
            newWidget.setMinimumSize(mapCanvasSize)

            # This is the new blank canvas that we will use the qpainter
            # from to draw the mapnik image over.
            self.qCanvas = QgsMapCanvas(self.iface.mainWindow())
            self.qCanvas.setCanvasColor(QColor(255, 255, 255))
            self.qCanvas.enableAntiAliasing(True)
            self.qCanvas.useImageToRender(False)
            self.qCanvas.show()

            # A set of map tools for the mapnik canvas
            self.qCanvasPan = QgsMapToolPan(self.qCanvas)
            self.qCanvasZoomIn = QgsMapToolZoom(self.qCanvas, False)
            self.qCanvasZoomOut = QgsMapToolZoom(self.qCanvas, True)
            self.mapToolSet(self.canvas.mapTool())

            # Add the canvas items to the tabs
            newWidget.addTab(self.canvas, "Main Map")
            newWidget.addTab(self.qCanvas, "Mapnik Rendered Map")
            self.tabWidget = newWidget
            # Add the tabs as the central widget
            self.iface.mainWindow().setCentralWidget(newWidget)
            # Need to set the extent of both canvases as we have just resized
            # things
            self.canvas.setExtent(mapCanvasExtent)
            self.qCanvas.setExtent(mapCanvasExtent)
            # Hook up to the tabs changing so we can make sure to update the
            # rendering in a lazy way... i.e. a pan in the main canvas will
            # not cause a redraw in the mapnik tab until the mapnik tab
            # is selected.
            self.connect(self.tabWidget, SIGNAL("currentChanged(int)"),
                         self.tabChanged)
            # Grab the maptool change signal so the mapnik canvas tool
            # can stay in sync.
            # TODO: We need to get the in/out property for the zoom tool
            # exposed to the python bindings.  As it stands now, we can
            # not tell what direction the tool is going when we get this
            # signal and it is a zoom tool.
            QObject.connect(self.canvas, SIGNAL("mapToolSet(QgsMapTool *)"),
                            self.mapToolSet)
            # Catch any mouse movements over the mapnik canvas and
            # sneek in and update the cord display
            ## This is a hack to find the status element to populate with xy
            self.mapCoords = self.findMapCoordsStatus()
            QObject.connect(self.qCanvas,
                            SIGNAL("xyCoordinates(const QgsPoint&)"),
                            self.updateCoordsDisplay)
            # Get the renderComplete signal for the qCanvas to allow us to
            # render the mapnik image over it.
            QObject.connect(self.qCanvas, SIGNAL("renderComplete(QPainter *)"),
                            self.render_dynamic)
            # Get the renderComplete signal for the main canvas so we can tell
            # if there have been any layer changes and if we need to re-draw
            # the mapnik image.  This is mainly for when the mapnik tab is
            # active but layer changes are happening.
            QObject.connect(self.canvas, SIGNAL("renderComplete(QPainter *)"),
                            self.checkLayers)
            QObject.connect(self.canvas, SIGNAL("extentsChanged()"),
                            self.checkExtentsChanged)
            self.using_mapnik = True
            # We use a single shot timer to let the main app resize the main
            # window with us holding a minsize we want, then we reset the
            # allowable min size after the main app has its turn.  Hack, but
            # allows for the window to be rezised with a new main widget.
            QTimer.singleShot(1, self.finishStartRendering)

    def updateCoordsDisplay(self, p):
        if self.mapCoords:
            capturePyString = "%.5f,%.5f" % (p.x(), p.y())
            capture_string = QString(capturePyString)
            self.mapCoords.setText(capture_string)

    def mapToolSet(self, tool):
        # something changed here in recent QGIS versions causing:
        # exceptions when closing QGIS because these objects are None
        if tool:
            if isinstance(tool, QgsMapToolPan):
                self.qCanvas.setMapTool(self.qCanvasPan)
            elif isinstance(tool, QgsMapToolZoom):
                # Yet another hack to find out if the tool we are using is a
                # zoom in or out
                if tool.action().text() == QString("Zoom In"):
                    self.qCanvas.setMapTool(self.qCanvasZoomIn)
                else:
                    self.qCanvas.setMapTool(self.qCanvasZoomOut)
            else:
                self.qCanvas.setMapTool(self.qCanvasPan)

    def switch_tab_qgis(self):
        if self.tabWidget:
            self.tabWidget.setCurrentIndex(0)

    def switch_tab_mapnik(self):
        if self.tabWidget:
            self.tabWidget.setCurrentIndex(1)

    def tabChanged(self, index):
        if index == 0:
            self.mapnikMapCoordChange()
        else:
            self.mainMapCoordChange()

    def mainMapCoordChange(self):
        # print "coordChange"
        self.mapnik_map = self.easyCanvas.to_mapnik(self.mapnik_map)
        self.qCanvas.setExtent(self.iface.mapCanvas().extent())
        self.qCanvas.refresh()

    def mapnikMapCoordChange(self):
        # print "coordChange"
        self.canvas.setExtent(self.qCanvas.extent())
        self.canvas.refresh()

    # Here we are checking to see if we got a new extent on the main
    # canvas even though we are in the mapnik tab... in that case we have
    # done something like zoom to full extent etc.
    def checkExtentsChanged(self):
        if self.tabWidget:
            if self.tabWidget.currentIndex() == 1:
                self.mainMapCoordChange()

    # Here we are checking to see if we got a render complete on the main
    # canvas even though we are in the mapnik tab... in that case we have
    # a new layer etc.
    def checkLayers(self, painter=None):
        if self.tabWidget:
            if self.tabWidget.currentIndex() == 1:
                # There was a change in the main canvas while we are viewing
                # the mapnik canvas (i.e. layer added/removed etc) so we
                # need to refresh the mapnik map
                self.mapnik_map = self.easyCanvas.to_mapnik(self.mapnik_map)
                self.qCanvas.refresh()
                # We also make sure the main map canvas gets put back to the
                # current extent of the qCanvas incase the main map got changed
                # as a side effect since updates to it are lazy loaded on tab
                # change.
                self.canvas.setExtent(self.qCanvas.extent())
                # We make sure to update the XML viewer if
                # if is open
                if self.dock_window:
                    self.view_xml(self.mapnik_map)
        if self.dock_window:
            self.view_xml()

    def render_dynamic(self, painter):
        if self.mapnik_map:
            w = painter.device().width()
            h = painter.device().height()
            # using canvas dims leads to shift in QGIS < 1.3...
            #w = self.canvas.width()
            #h = self.canvas.height()
            try:
                self.mapnik_map.resize(w, h)
            except:
                self.mapnik_map.width = w
                self.mapnik_map.height = h
            if self.qCanvas:
                can = self.qCanvas
            else:
                can = self.canvas
            try:
                e = can.extent()
            except:
                can = self.canvas
                e = can.extent()
            bbox = mapnik.Envelope(e.xMinimum(), e.yMinimum(), e.xMaximum(),
                                   e.yMaximum())
            self.mapnik_map.zoom_to_box(bbox)
            im = mapnik.Image(w, h)
            mapnik.render(self.mapnik_map, im)
            if os.name == 'nt':
                qim = QImage()
                qim.loadFromData(QByteArray(im.tostring('png')))
                painter.drawImage(0, 0, qim)
            else:
                qim = QImage(im.tostring(), w, h, QImage.Format_ARGB32)
                painter.drawImage(0, 0, qim.rgbSwapped())
            can.refresh()