mirror of
				https://github.com/liberatedsystems/Sideband_CE.git
				synced 2024-09-03 04:13:27 +02:00 
			
		
		
		
	Added map settings and layer switching
This commit is contained in:
		
							parent
							
								
									0a2a87775e
								
							
						
					
					
						commit
						86e053cdb9
					
				
							
								
								
									
										203
									
								
								sbapp/main.py
									
									
									
									
									
								
							
							
						
						
									
										203
									
								
								sbapp/main.py
									
									
									
									
									
								
							@ -15,6 +15,7 @@ import RNS
 | 
			
		||||
import LXMF
 | 
			
		||||
import time
 | 
			
		||||
import os
 | 
			
		||||
import pathlib
 | 
			
		||||
import plyer
 | 
			
		||||
import base64
 | 
			
		||||
import threading
 | 
			
		||||
@ -149,7 +150,11 @@ class SidebandApp(MDApp):
 | 
			
		||||
 | 
			
		||||
        self.conversations_view = None
 | 
			
		||||
        self.messages_view = None
 | 
			
		||||
        self.map = None
 | 
			
		||||
        self.map_layer = None
 | 
			
		||||
        self.map_screen = None
 | 
			
		||||
        self.offline_source = None
 | 
			
		||||
        self.map_settings_screen = None
 | 
			
		||||
        self.object_details_screen = None
 | 
			
		||||
        self.sync_dialog = None
 | 
			
		||||
        self.settings_ready = False
 | 
			
		||||
@ -3278,71 +3283,155 @@ class SidebandApp(MDApp):
 | 
			
		||||
 | 
			
		||||
    def map_fm_got_path(self, path):
 | 
			
		||||
        self.map_fm_exited()
 | 
			
		||||
        # TODO: Remove
 | 
			
		||||
        toast(path)
 | 
			
		||||
        ###
 | 
			
		||||
        try:
 | 
			
		||||
            source = MBTilesMapSource(path)
 | 
			
		||||
            self.map_update_source(source)
 | 
			
		||||
            self.map_update_source()
 | 
			
		||||
            self.sideband.config["map_storage_file"] = path
 | 
			
		||||
            self.sideband.config["map_storage_path"] = str(pathlib.Path(path).parent.resolve())
 | 
			
		||||
            self.sideband.save_configuration()
 | 
			
		||||
            toast("Using \""+os.path.basename(path)+"\" as offline map")
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            RNS.log(f"Error while loading map \"{path}\": "+str(e), RNS.LOG_ERROR)
 | 
			
		||||
            selfmap_update_source()
 | 
			
		||||
            toast("Could not load map \""+os.path.basename(path)+"\"")
 | 
			
		||||
            self.sideband.config["map_storage_file"] = None
 | 
			
		||||
            self.sideband.config["map_use_offline"] = False
 | 
			
		||||
            self.sideband.config["map_use_online"] = True
 | 
			
		||||
            self.map_settings_load_states()
 | 
			
		||||
            self.map_update_source()
 | 
			
		||||
 | 
			
		||||
    def map_fm_exited(self, *args):
 | 
			
		||||
        RNS.log("Closing file manager", RNS.LOG_DEBUG)
 | 
			
		||||
        self.sideband.config["map_storage_file"] = None
 | 
			
		||||
        self.sideband.save_configuration()
 | 
			
		||||
        self.manager_open = False
 | 
			
		||||
        self.file_manager.close()
 | 
			
		||||
        self.map_update_source()
 | 
			
		||||
 | 
			
		||||
    def map_get_source(self):
 | 
			
		||||
        current_map_path = self.sideband.config["map_storage_file"]
 | 
			
		||||
        source = None
 | 
			
		||||
 | 
			
		||||
        if current_map_path:
 | 
			
		||||
    def map_get_offline_source(self):
 | 
			
		||||
        if self.offline_source != None:
 | 
			
		||||
            return self.offline_source
 | 
			
		||||
        else:
 | 
			
		||||
            try:
 | 
			
		||||
                current_map_path = self.sideband.config["map_storage_file"]
 | 
			
		||||
                if current_map_path == None:
 | 
			
		||||
                    raise ValueError("Map path cannot be None")
 | 
			
		||||
                source = MBTilesMapSource(current_map_path)
 | 
			
		||||
                self.offline_source = source
 | 
			
		||||
                return self.offline_source
 | 
			
		||||
            
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                RNS.log(f"Error while loading map from \"{current_map_path}\": "+str(e))
 | 
			
		||||
                self.sideband.config["map_storage_file"] = None
 | 
			
		||||
                self.sideband.config["map_use_offline"] = False
 | 
			
		||||
                self.sideband.config["map_use_online"] = True
 | 
			
		||||
                self.sideband.save_configuration()
 | 
			
		||||
                self.map_settings_load_states()
 | 
			
		||||
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
    def map_get_source(self):
 | 
			
		||||
        source = None
 | 
			
		||||
        if self.sideband.config["map_use_offline"]:
 | 
			
		||||
            source = self.map_get_offline_source()
 | 
			
		||||
        
 | 
			
		||||
        if source == None:
 | 
			
		||||
            source = MapSource.from_provider("osm")
 | 
			
		||||
            source = MapSource.from_provider("osm", quad_key=False)
 | 
			
		||||
 | 
			
		||||
        return source
 | 
			
		||||
 | 
			
		||||
    def map_update_source(self, source=None):
 | 
			
		||||
        ns = source or self.map_get_source()
 | 
			
		||||
        if self.map != None:
 | 
			
		||||
            
 | 
			
		||||
            if source != None:
 | 
			
		||||
                maxz = source.max_zoom
 | 
			
		||||
                minz = source.min_zoom
 | 
			
		||||
                if self.map.zoom > maxz:
 | 
			
		||||
                    self.map.zoom = maxz
 | 
			
		||||
                
 | 
			
		||||
                if self.map.zoom < minz:
 | 
			
		||||
                    self.map.zoom = minz
 | 
			
		||||
 | 
			
		||||
                m = self.map
 | 
			
		||||
                RNS.log(f"Map {m.lat} {m.lon} {m.zoom}")
 | 
			
		||||
                nlat = self.map.lat
 | 
			
		||||
                nlon = self.map.lon
 | 
			
		||||
                if nlat < -89: nlat = -89
 | 
			
		||||
                if nlat > 89: nlat = 89
 | 
			
		||||
                if nlon < -179: nlon = -179
 | 
			
		||||
                if nlon > 179: nlon = 179
 | 
			
		||||
                self.map.center_on(nlat,nlon)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            self.map.map_source = ns
 | 
			
		||||
 | 
			
		||||
    def map_test_action(self, sender=None):
 | 
			
		||||
    def map_layers_action(self, sender=None):
 | 
			
		||||
        try:
 | 
			
		||||
            ml = self.map_layer
 | 
			
		||||
            layers = []
 | 
			
		||||
            if self.sideband.config["map_use_offline"]:
 | 
			
		||||
                layers.append("offline")
 | 
			
		||||
 | 
			
		||||
            if self.sideband.config["map_use_online"]:
 | 
			
		||||
                layers.append("osm")
 | 
			
		||||
                layers.append("ve")
 | 
			
		||||
 | 
			
		||||
            if ml == None: ml = layers[0]
 | 
			
		||||
 | 
			
		||||
            if not ml in layers:
 | 
			
		||||
                ml = layers[0]
 | 
			
		||||
 | 
			
		||||
            mli = layers.index(ml)
 | 
			
		||||
            mli = (mli+1)%len(layers)
 | 
			
		||||
            ml = layers[mli]
 | 
			
		||||
 | 
			
		||||
            source = None
 | 
			
		||||
            if ml == "offline": source = self.map_get_offline_source()
 | 
			
		||||
            if ml == "osm": source = MapSource.from_provider("osm", quad_key=False)
 | 
			
		||||
            if ml == "ve": source = MapSource.from_provider("ve", quad_key=True)
 | 
			
		||||
 | 
			
		||||
            if source != None:
 | 
			
		||||
                self.map_layer = ml
 | 
			
		||||
                self.map_update_source(source)
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            RNS.log("Error while switching map layer: "+str(e), RNS.LOG_ERROR)
 | 
			
		||||
 | 
			
		||||
    def map_select_file_action(self, sender=None):
 | 
			
		||||
        perm_ok = False
 | 
			
		||||
        if RNS.vendor.platformutils.is_android():
 | 
			
		||||
            perm_ok = self.check_storage_permission()
 | 
			
		||||
        if self.sideband.config["map_storage_path"] == None:
 | 
			
		||||
            if RNS.vendor.platformutils.is_android():
 | 
			
		||||
                perm_ok = self.check_storage_permission()
 | 
			
		||||
 | 
			
		||||
            if self.sideband.config["map_storage_external"]:
 | 
			
		||||
                path = secondary_external_storage_path()
 | 
			
		||||
                if path == None: path = primary_external_storage_path()
 | 
			
		||||
            else: 
 | 
			
		||||
                path = primary_external_storage_path()
 | 
			
		||||
                if self.sideband.config["map_storage_external"]:
 | 
			
		||||
                    path = secondary_external_storage_path()
 | 
			
		||||
                    if path == None: path = primary_external_storage_path()
 | 
			
		||||
                else: 
 | 
			
		||||
                    path = primary_external_storage_path()
 | 
			
		||||
 | 
			
		||||
            else:
 | 
			
		||||
                perm_ok = True
 | 
			
		||||
                if self.sideband.config["map_storage_external"]:
 | 
			
		||||
                    path = "/"
 | 
			
		||||
                else:
 | 
			
		||||
                    path = os.path.expanduser("~")
 | 
			
		||||
        else:
 | 
			
		||||
            perm_ok = True
 | 
			
		||||
            path = os.path.expanduser("~")
 | 
			
		||||
            path = self.sideband.config["map_storage_path"]
 | 
			
		||||
 | 
			
		||||
        if perm_ok and path != None:
 | 
			
		||||
            self.file_manager = MDFileManager(
 | 
			
		||||
                exit_manager=self.map_fm_exited,
 | 
			
		||||
                select_path=self.map_fm_got_path,
 | 
			
		||||
            )
 | 
			
		||||
            self.file_manager.ext = [".mbtiles"]
 | 
			
		||||
            self.file_manager.show(path)
 | 
			
		||||
            try:
 | 
			
		||||
                self.file_manager = MDFileManager(
 | 
			
		||||
                    exit_manager=self.map_fm_exited,
 | 
			
		||||
                    select_path=self.map_fm_got_path,
 | 
			
		||||
                )
 | 
			
		||||
                self.file_manager.ext = [".mbtiles"]
 | 
			
		||||
                self.file_manager.show(path)
 | 
			
		||||
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                self.sideband.config["map_storage_path"] = None
 | 
			
		||||
                self.sideband.save_configuration()
 | 
			
		||||
                toast("Error reading directory, check permissions!")
 | 
			
		||||
        
 | 
			
		||||
        else:
 | 
			
		||||
            self.sideband.config["map_storage_path"] = None
 | 
			
		||||
            self.sideband.save_configuration()
 | 
			
		||||
            toast("No file access, check permissions!")
 | 
			
		||||
 | 
			
		||||
    def map_action(self, sender=None, direction="left"):
 | 
			
		||||
@ -3371,6 +3460,60 @@ class SidebandApp(MDApp):
 | 
			
		||||
            self.map_update_markers()
 | 
			
		||||
        Clock.schedule_once(am_job, 0.6)
 | 
			
		||||
 | 
			
		||||
    def map_settings_load_states(self):
 | 
			
		||||
        if self.map_settings_screen != None:
 | 
			
		||||
            self.map_settings_screen.ids.map_use_online.active = self.sideband.config["map_use_online"]
 | 
			
		||||
            self.map_settings_screen.ids.map_use_offline.active = self.sideband.config["map_use_offline"]
 | 
			
		||||
            self.map_settings_screen.ids.map_storage_external.active = self.sideband.config["map_storage_external"]
 | 
			
		||||
 | 
			
		||||
    def map_settings_init(self):
 | 
			
		||||
        self.map_settings_load_states()
 | 
			
		||||
        def map_settings_save(sender=None, event=None):
 | 
			
		||||
            self.sideband.config["map_storage_external"] = self.map_settings_screen.ids.map_storage_external.active
 | 
			
		||||
            self.sideband.config["map_use_online"] = self.map_settings_screen.ids.map_use_online.active
 | 
			
		||||
            self.sideband.config["map_use_offline"] = self.map_settings_screen.ids.map_use_offline.active
 | 
			
		||||
            self.sideband.save_configuration()
 | 
			
		||||
 | 
			
		||||
        def external_toggle(sender=None, event=None):
 | 
			
		||||
            self.sideband.config["map_storage_path"] = None
 | 
			
		||||
            map_settings_save()
 | 
			
		||||
 | 
			
		||||
        def offline_toggle(sender=None, event=None):
 | 
			
		||||
            if self.map_settings_screen.ids.map_use_offline.active:
 | 
			
		||||
                # self.map_settings_screen.ids.map_use_online.active = False
 | 
			
		||||
                if self.sideband.config["map_storage_file"] == None:
 | 
			
		||||
                    self.map_select_file_action()
 | 
			
		||||
            else:
 | 
			
		||||
                self.map_settings_screen.ids.map_use_online.active = True
 | 
			
		||||
            map_settings_save(); self.map_update_source()
 | 
			
		||||
 | 
			
		||||
        def online_toggle(sender=None, event=None):
 | 
			
		||||
            if self.map_settings_screen.ids.map_use_online.active:
 | 
			
		||||
                # self.map_settings_screen.ids.map_use_offline.active = False
 | 
			
		||||
                pass
 | 
			
		||||
            else:
 | 
			
		||||
                self.map_settings_screen.ids.map_use_offline.active = True
 | 
			
		||||
            map_settings_save(); self.map_update_source()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        self.map_settings_screen.ids.map_use_offline.bind(active=offline_toggle)
 | 
			
		||||
        self.map_settings_screen.ids.map_use_online.bind(active=online_toggle)
 | 
			
		||||
        self.map_settings_screen.ids.map_storage_external.bind(active=external_toggle)
 | 
			
		||||
 | 
			
		||||
    def map_settings_action(self, sender=None, direction="left"):
 | 
			
		||||
        if not self.root.ids.screen_manager.has_screen("map_settings_screen"):
 | 
			
		||||
            self.map_settings_screen = Builder.load_string(layout_map_settings_screen)
 | 
			
		||||
            self.map_settings_screen.app = self
 | 
			
		||||
            self.root.ids.screen_manager.add_widget(self.map_settings_screen)
 | 
			
		||||
            self.map_settings_screen.ids.map_config_info.text = "\n\nSideband can use map sources from the Internet, or a map source stored locally on this device in MBTiles format."
 | 
			
		||||
            self.map_settings_screen.ids.map_settings_scrollview.effect_cls = ScrollEffect
 | 
			
		||||
            self.map_settings_init()
 | 
			
		||||
 | 
			
		||||
        self.root.ids.screen_manager.transition.direction = direction
 | 
			
		||||
        self.root.ids.screen_manager.current = "map_settings_screen"
 | 
			
		||||
        self.root.ids.nav_drawer.set_state("closed")
 | 
			
		||||
        self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current)
 | 
			
		||||
 | 
			
		||||
    def close_location_error_dialog(self, sender=None):
 | 
			
		||||
        if hasattr(self, "location_error_dialog") and self.location_error_dialog != None:
 | 
			
		||||
            self.location_error_dialog.dismiss()
 | 
			
		||||
@ -3391,7 +3534,7 @@ class SidebandApp(MDApp):
 | 
			
		||||
        if not location:
 | 
			
		||||
            self.location_error_dialog = MDDialog(
 | 
			
		||||
                title="No Location",
 | 
			
		||||
                text="During the last 24 hours, no location updates have been received from this peer. You can use the the [b]Situation Map[/b] to manually search for earlier telemetry.",
 | 
			
		||||
                text="No location updates have been received from this peer. You can use the the [b]Situation Map[/b] to manually search for earlier telemetry.",
 | 
			
		||||
                buttons=[
 | 
			
		||||
                    MDRectangleFlatButton(
 | 
			
		||||
                        text="OK",
 | 
			
		||||
 | 
			
		||||
@ -171,8 +171,13 @@ class SidebandCore():
 | 
			
		||||
        try:
 | 
			
		||||
            if not os.path.isfile(self.config_path):
 | 
			
		||||
                self.__init_config()
 | 
			
		||||
            else:
 | 
			
		||||
                self.__load_config()
 | 
			
		||||
            else:
 | 
			
		||||
                try:
 | 
			
		||||
                    self.__load_config()
 | 
			
		||||
                except Exception as e:
 | 
			
		||||
                    self.__init_config()
 | 
			
		||||
                    self.__load_config()
 | 
			
		||||
                self.first_run = False
 | 
			
		||||
 | 
			
		||||
            if self.config["debug"]:
 | 
			
		||||
@ -533,6 +538,13 @@ class SidebandCore():
 | 
			
		||||
            self.config["map_zoom"] = 3
 | 
			
		||||
        if not "map_storage_external" in self.config:
 | 
			
		||||
            self.config["map_storage_external"] = False
 | 
			
		||||
        if not "map_use_offline" in self.config:
 | 
			
		||||
            self.config["map_use_offline"] = False
 | 
			
		||||
        if not "map_use_online" in self.config:
 | 
			
		||||
            self.config["map_use_online"] = True
 | 
			
		||||
        if not "map_layer" in self.config:
 | 
			
		||||
            self.config["map_layer"] = None
 | 
			
		||||
        
 | 
			
		||||
        if not "map_storage_path" in self.config:
 | 
			
		||||
            self.config["map_storage_path"] = None
 | 
			
		||||
        if not "map_storage_file" in self.config:
 | 
			
		||||
@ -814,7 +826,7 @@ class SidebandCore():
 | 
			
		||||
        return []
 | 
			
		||||
 | 
			
		||||
    def peer_location(self, context_dest):
 | 
			
		||||
        after_time = time.time()-24*60*60
 | 
			
		||||
        after_time = time.time()-3*30*24*60*60
 | 
			
		||||
        pts = self.peer_telemetry(context_dest, after=after_time)
 | 
			
		||||
        for pt in pts:
 | 
			
		||||
            try:
 | 
			
		||||
 | 
			
		||||
@ -851,6 +851,117 @@ MDScreen:
 | 
			
		||||
                        height: dp(256)
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
layout_map_settings_screen = """
 | 
			
		||||
MDScreen:
 | 
			
		||||
    name: "map_settings_screen"
 | 
			
		||||
    
 | 
			
		||||
    BoxLayout:
 | 
			
		||||
        orientation: "vertical"
 | 
			
		||||
 | 
			
		||||
        MDTopAppBar:
 | 
			
		||||
            title: "Map Configuration"
 | 
			
		||||
            anchor_title: "left"
 | 
			
		||||
            elevation: 0
 | 
			
		||||
            left_action_items:
 | 
			
		||||
                [['menu', lambda x: root.app.nav_drawer.set_state("open")]]
 | 
			
		||||
            right_action_items:
 | 
			
		||||
                [
 | 
			
		||||
                ['close', lambda x: root.app.close_sub_map_action(self)],
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
        ScrollView:
 | 
			
		||||
            id: map_settings_scrollview
 | 
			
		||||
 | 
			
		||||
            MDBoxLayout:
 | 
			
		||||
                orientation: "vertical"
 | 
			
		||||
                size_hint_y: None
 | 
			
		||||
                height: self.minimum_height
 | 
			
		||||
                padding: [dp(28), dp(48), dp(28), dp(16)]
 | 
			
		||||
 | 
			
		||||
                MDLabel:
 | 
			
		||||
                    text: "Configure Map"
 | 
			
		||||
                    font_style: "H6"
 | 
			
		||||
 | 
			
		||||
                MDLabel:
 | 
			
		||||
                    id: map_config_info
 | 
			
		||||
                    markup: True
 | 
			
		||||
                    text: ""
 | 
			
		||||
                    size_hint_y: None
 | 
			
		||||
                    text_size: self.width, None
 | 
			
		||||
                    height: self.texture_size[1]
 | 
			
		||||
 | 
			
		||||
                MDLabel:
 | 
			
		||||
                    markup: True
 | 
			
		||||
                    text: "\\n"
 | 
			
		||||
                    size_hint_y: None
 | 
			
		||||
                    text_size: self.width, None
 | 
			
		||||
                    height: self.texture_size[1]
 | 
			
		||||
 | 
			
		||||
                MDBoxLayout:
 | 
			
		||||
                    orientation: "horizontal"
 | 
			
		||||
                    padding: [0,0,dp(24),0]
 | 
			
		||||
                    size_hint_y: None
 | 
			
		||||
                    height: dp(48)
 | 
			
		||||
                    
 | 
			
		||||
                    MDLabel:
 | 
			
		||||
                        text: "Use online map sources"
 | 
			
		||||
                        font_style: "H6"
 | 
			
		||||
 | 
			
		||||
                    MDSwitch:
 | 
			
		||||
                        id: map_use_online
 | 
			
		||||
                        pos_hint: {"center_y": 0.3}
 | 
			
		||||
                        active: False
 | 
			
		||||
 | 
			
		||||
                MDBoxLayout:
 | 
			
		||||
                    orientation: "horizontal"
 | 
			
		||||
                    padding: [0,0,dp(24),0]
 | 
			
		||||
                    size_hint_y: None
 | 
			
		||||
                    height: dp(48)
 | 
			
		||||
                    
 | 
			
		||||
                    MDLabel:
 | 
			
		||||
                        text: "Use offline map source"
 | 
			
		||||
                        font_style: "H6"
 | 
			
		||||
 | 
			
		||||
                    MDSwitch:
 | 
			
		||||
                        id: map_use_offline
 | 
			
		||||
                        pos_hint: {"center_y": 0.3}
 | 
			
		||||
                        active: False
 | 
			
		||||
 | 
			
		||||
                MDBoxLayout:
 | 
			
		||||
                    orientation: "horizontal"
 | 
			
		||||
                    padding: [0,0,dp(24),0]
 | 
			
		||||
                    size_hint_y: None
 | 
			
		||||
                    height: dp(48)
 | 
			
		||||
                    
 | 
			
		||||
                    MDLabel:
 | 
			
		||||
                        id: map_storage_external_label
 | 
			
		||||
                        text: "Use external storage path"
 | 
			
		||||
                        font_style: "H6"
 | 
			
		||||
 | 
			
		||||
                    MDSwitch:
 | 
			
		||||
                        id: map_storage_external
 | 
			
		||||
                        pos_hint: {"center_y": 0.3}
 | 
			
		||||
                        active: False
 | 
			
		||||
 | 
			
		||||
                MDBoxLayout:
 | 
			
		||||
                    orientation: "vertical"
 | 
			
		||||
                    size_hint_y: None
 | 
			
		||||
                    height: self.minimum_height
 | 
			
		||||
                    padding: [0, dp(24), 0, 0]
 | 
			
		||||
 | 
			
		||||
                    MDRectangleFlatIconButton:
 | 
			
		||||
                        id: telemetry_icons_button
 | 
			
		||||
                        icon: "list-box-outline"
 | 
			
		||||
                        text: "Select MBTiles Map"
 | 
			
		||||
                        padding: [dp(0), dp(14), dp(0), dp(14)]
 | 
			
		||||
                        icon_size: dp(24)
 | 
			
		||||
                        font_size: dp(16)
 | 
			
		||||
                        size_hint: [1.0, None]
 | 
			
		||||
                        on_release: root.app.map_select_file_action(self)
 | 
			
		||||
                        disabled: False
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
layout_map_screen = """
 | 
			
		||||
MDScreen:
 | 
			
		||||
    name: "map_screen"
 | 
			
		||||
@ -869,7 +980,8 @@ MDScreen:
 | 
			
		||||
                ['format-list-bulleted-type', lambda x: root.app.map_object_list(self)],
 | 
			
		||||
                ['arrow-down-bold-hexagon-outline', lambda x: root.app.telemetry_request_action(self)],
 | 
			
		||||
                ['upload-lock', lambda x: root.app.telemetry_send_update(self)],
 | 
			
		||||
                ['wrench-cog', lambda x: root.app.map_test_action(self)],
 | 
			
		||||
                ['layers', lambda x: root.app.map_layers_action(self)],
 | 
			
		||||
                ['wrench-cog', lambda x: root.app.map_settings_action(self)],
 | 
			
		||||
                ['close', lambda x: root.app.close_any_action(self)],
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user