def make_gui_mouse(self, parent): """Build the mouse part of the controls part of GUI. parent reference to parent Returns reference to containing sizer object. """ # create objects txt = wx.StaticText(parent, wx.ID_ANY, 'Lon/Lat: ') self.mouse_position = ROTextCtrl(parent, '', size=(150, -1), tooltip=('Shows the mouse ' 'longitude and latitude ' 'on the map')) # lay out the controls sb = AppStaticBox(parent, 'Mouse position') box = wx.StaticBoxSizer(sb, orient=wx.HORIZONTAL) box.Add(txt, border=PackBorder, flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT)) box.Add(self.mouse_position, proportion=1, border=PackBorder, flag=wx.RIGHT | wx.TOP | wx.BOTTOM) return box
def make_gui_level(self, parent): """Build the control that shows the level. parent reference to parent Returns reference to containing sizer object. """ # create objects txt = wx.StaticText(parent, wx.ID_ANY, 'Level: ') self.map_level = ROTextCtrl(parent, '', size=(30,-1), tooltip='Shows map zoom level') # lay out the controls sb = AppStaticBox(parent, 'Map level') box = wx.StaticBoxSizer(sb, orient=wx.HORIZONTAL) box.Add(txt, flag=(wx.ALIGN_CENTER_VERTICAL |wx.LEFT)) box.Add(self.map_level, proportion=0, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL) return box
class AppFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, size=DefaultAppSize, title=DemoName) self.SetMinSize(DefaultAppSize) self.panel = wx.Panel(self, wx.ID_ANY) self.panel.SetBackgroundColour(wx.WHITE) self.panel.ClearBackground() self.tile_source = tiles.Tiles() self.tile_directory = self.tile_source.tiles_dir # the data objects for map and view layers self.map_layer = None self.view_layer = None # build the GUI self.make_gui(self.panel) # finally, set up application window position self.Centre() # bind events to handlers self.pyslip.Bind(pyslip.EVT_PYSLIP_POSITION, self.handle_position_event) self.pyslip.Bind(pyslip.EVT_PYSLIP_LEVEL, self.handle_level_change) # finally, goto desired level and position wx.CallLater(25, self.final_setup, InitViewLevel, InitViewPosition) def final_setup(self, level, position): """Perform final setup. level zoom level required position position to be in centre of view We do this in a CallAfter() function for those operations that must not be done while the GUI is "fluid". """ self.pyslip.GotoLevelAndPosition(level, position) ##### # Build the GUI ##### def make_gui(self, parent): """Create application GUI.""" # start application layout all_display = wx.BoxSizer(wx.HORIZONTAL) parent.SetSizer(all_display) # put map view in left of horizontal box self.pyslip = pyslip.pySlip(parent, tile_src=self.tile_source, style=wx.SIMPLE_BORDER) all_display.Add(self.pyslip, proportion=1, border=1, flag=wx.EXPAND) # small spacer here - separate view and controls all_display.AddSpacer(HSpacerSize) # add controls to right of spacer controls = self.make_gui_controls(parent) all_display.Add(controls, proportion=0, border=1) parent.SetSizerAndFit(all_display) def make_gui_view(self, parent): """Build the map view widget parent reference to the widget parent Returns the static box sizer. """ # create gui objects sb = AppStaticBox(parent, '') # tile_object = tiles.Tiles(self.tile_directory) self.pyslip = pyslip.pySlip(parent, tile_src=self.tile_source) # lay out objects box = wx.StaticBoxSizer(sb, orient=wx.HORIZONTAL) box.Add(self.pyslip, proportion=1, border=1, flag=wx.EXPAND) return box def make_gui_controls(self, parent): """Build the 'controls' part of the GUI parent reference to parent Returns reference to containing sizer object. """ # all controls in vertical box sizer controls = wx.BoxSizer(wx.VERTICAL) # add the map level in use widget level = self.make_gui_level(parent) controls.Add(level, proportion=0, flag=wx.EXPAND | wx.ALL) # vertical spacer controls.AddSpacer(VSpacerSize) # add the mouse position feedback stuff mouse = self.make_gui_mouse(parent) controls.Add(mouse, proportion=0, flag=wx.EXPAND | wx.ALL) # buttons for each point of interest self.buttons = {} for (num, city) in enumerate(Cities): controls.AddSpacer(VSpacerSize) (lonlat, name) = city btn = wx.Button(parent, num, name) controls.Add(btn, proportion=0, flag=wx.EXPAND | wx.ALL) btn.Bind(wx.EVT_BUTTON, self.handle_button) self.buttons[num] = city return controls def make_gui_level(self, parent): """Build the control that shows the level. parent reference to parent Returns reference to containing sizer object. """ # create objects txt = wx.StaticText(parent, wx.ID_ANY, 'Level: ') self.map_level = wx.StaticText(parent, wx.ID_ANY, ' ') # lay out the controls sb = AppStaticBox(parent, 'Map level') box = wx.StaticBoxSizer(sb, orient=wx.HORIZONTAL) box.Add(txt, border=PackBorder, flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT)) box.Add(self.map_level, proportion=0, border=PackBorder, flag=wx.RIGHT | wx.TOP) return box def make_gui_mouse(self, parent): """Build the mouse part of the controls part of GUI. parent reference to parent Returns reference to containing sizer object. """ # create objects txt = wx.StaticText(parent, wx.ID_ANY, 'Lon/Lat: ') self.mouse_position = ROTextCtrl(parent, '', size=(150, -1), tooltip=('Shows the mouse ' 'longitude and latitude ' 'on the map')) # lay out the controls sb = AppStaticBox(parent, 'Mouse position') box = wx.StaticBoxSizer(sb, orient=wx.HORIZONTAL) box.Add(txt, border=PackBorder, flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT)) box.Add(self.mouse_position, proportion=1, border=PackBorder, flag=wx.RIGHT | wx.TOP | wx.BOTTOM) return box ###### # Exception handlers ###### def handle_button(self, event): """Handle button event.""" (posn, name) = self.buttons[event.Id] self.pyslip.GotoPosition(posn) if self.map_layer: self.pyslip.DeleteLayer(self.map_layer) map_data = [posn] point_colour = '#0000ff40' self.map_layer = self.pyslip.AddPointLayer(map_data, map_rel=True, placement='cc', color=point_colour, radius=11, visible=True, name='map_layer') if self.view_layer: self.pyslip.DeleteLayer(self.view_layer) view_data = [( ((0, 0), (0, -10), (0, 0), (0, 10), (0, 0), (-10, 0), (0, 0), (10, 0)), { 'colour': '#ff0000ff' }, )] # poly_colour = '#ff0000ff' self.view_layer = self.pyslip.AddPolygonLayer( view_data, map_rel=False, placement='cc', # colour=poly_colour, closed=False, visible=True, name='view_layer') def handle_position_event(self, event): """Handle a pySlip POSITION event.""" posn_str = '' if event.mposn: (lon, lat) = event.mposn posn_str = ('%.*f / %.*f' % (LonLatPrecision, lon, LonLatPrecision, lat)) self.mouse_position.SetValue(posn_str) def handle_level_change(self, event): """Handle a pySlip LEVEL event.""" self.map_level.SetLabel('%d' % event.level)
class AppFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, None, size=DefaultAppSize, title='%s, test version %s' % (DemoName, DemoVersion)) self.SetMinSize(DefaultAppSize) self.panel = wx.Panel(self, wx.ID_ANY) self.panel.SetBackgroundColour(wx.WHITE) self.panel.ClearBackground() self.tile_source = Tiles.Tiles() # build the GUI self.make_gui(self.panel) # set initial view position self.map_level.SetLabel('%d' % InitialViewLevel) wx.CallAfter(self.final_setup, InitialViewLevel, InitialViewPosition) # force pyslip initialisation self.pyslip.OnSize() # finally, set up application window position self.Centre() # initialise state variables self.text_layer = None self.text_view_layer = None # finally, bind pySlip events to handlers self.pyslip.Bind(pyslip.EVT_PYSLIP_POSITION, self.handle_position_event) self.pyslip.Bind(pyslip.EVT_PYSLIP_LEVEL, self.handle_level_change) ##### # Build the GUI ##### def make_gui(self, parent): """Create application GUI.""" # start application layout all_display = wx.BoxSizer(wx.HORIZONTAL) parent.SetSizer(all_display) # put map view in left of horizontal box self.pyslip = pyslip.pySlip(parent, tile_src=self.tile_source, style=wx.SIMPLE_BORDER) all_display.Add(self.pyslip, proportion=1, border=0, flag=wx.EXPAND) # small spacer here - separate view and controls all_display.AddSpacer(HSpacerSize) # add controls to right of spacer controls = self.make_gui_controls(parent) all_display.Add(controls, proportion=0, border=0) parent.SetSizerAndFit(all_display) def make_gui_view(self, parent): """Build the map view widget parent reference to the widget parent Returns the static box sizer. """ # create gui objects sb = AppStaticBox(parent, '') self.pyslip = pyslip.pySlip(parent, tile_src=self.tile_source) # lay out objects box = wx.StaticBoxSizer(sb, orient=wx.HORIZONTAL) box.Add(self.pyslip, proportion=1, border=0, flag=wx.EXPAND) return box def make_gui_controls(self, parent): """Build the 'controls' part of the GUI parent reference to parent Returns reference to containing sizer object. """ # all controls in vertical box sizer controls = wx.BoxSizer(wx.VERTICAL) # add the map level in use widget level = self.make_gui_level(parent) controls.Add(level, proportion=0, flag=wx.EXPAND | wx.ALL) # vertical spacer controls.AddSpacer(VSpacerSize) # add the mouse position feedback stuff mouse = self.make_gui_mouse(parent) controls.Add(mouse, proportion=0, flag=wx.EXPAND | wx.ALL) # vertical spacer controls.AddSpacer(VSpacerSize) # controls for map-relative text layer self.text = self.make_gui_text(parent) controls.Add(self.text, proportion=0, flag=wx.EXPAND | wx.ALL) self.text.Bind(EVT_DELETE, self.textDelete) self.text.Bind(EVT_UPDATE, self.textUpdate) # vertical spacer controls.AddSpacer(VSpacerSize) # controls for view-relative text layer self.text_view = self.make_gui_text_view(parent) controls.Add(self.text_view, proportion=0, flag=wx.EXPAND | wx.ALL) self.text_view.Bind(EVT_DELETE, self.textViewDelete) self.text_view.Bind(EVT_UPDATE, self.textViewUpdate) # vertical spacer controls.AddSpacer(VSpacerSize) return controls def make_gui_level(self, parent): """Build the control that shows the level. parent reference to parent Returns reference to containing sizer object. """ # create objects txt = wx.StaticText(parent, wx.ID_ANY, 'Level: ') self.map_level = wx.StaticText(parent, wx.ID_ANY, ' ') # lay out the controls sb = AppStaticBox(parent, 'Map level') box = wx.StaticBoxSizer(sb, orient=wx.HORIZONTAL) box.Add(txt, border=PackBorder, flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT)) box.Add(self.map_level, proportion=0, border=PackBorder, flag=wx.RIGHT | wx.TOP) return box def make_gui_mouse(self, parent): """Build the mouse part of the controls part of GUI. parent reference to parent Returns reference to containing sizer object. """ # create objects txt = wx.StaticText(parent, wx.ID_ANY, 'Lon/Lat: ') self.mouse_position = ROTextCtrl(parent, '', size=(150, -1), tooltip=('Shows the mouse ' 'longitude and latitude ' 'on the map')) # lay out the controls sb = AppStaticBox(parent, 'Mouse position') box = wx.StaticBoxSizer(sb, orient=wx.HORIZONTAL) box.Add(txt, border=PackBorder, flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT | wx.LEFT)) box.Add(self.mouse_position, proportion=1, border=PackBorder, flag=wx.RIGHT | wx.TOP | wx.BOTTOM) return box def make_gui_text(self, parent): """Build the text part of the controls part of GUI. parent reference to parent Returns reference to containing sizer object. """ # create widgets text_obj = LayerControl(parent, 'Text, map-relative', text=DefaultText, font=DefaultFont, fontsize=DefaultFontSize, textcolour=DefaultTextColour, pointradius=DefaultPointRadius, pointcolour=DefaultPointColour, placement=DefaultPlacement, x=DefaultX, y=DefaultY, offset_x=DefaultOffsetX, offset_y=DefaultOffsetY) return text_obj def make_gui_text_view(self, parent): """Build the view-relative text part of the controls part of GUI. parent reference to parent Returns reference to containing sizer object. """ # create widgets text_obj = LayerControl(parent, 'Text, view-relative', text=DefaultViewText, font=DefaultFont, fontsize=DefaultFontSize, textcolour=DefaultTextColour, pointradius=DefaultPointRadius, pointcolour=DefaultPointColour, placement=DefaultViewPlacement, x=DefaultViewX, y=DefaultViewY, offset_x=DefaultViewOffsetX, offset_y=DefaultViewOffsetY) return text_obj ###### # event handlers ###### ##### map-relative text layer def textUpdate(self, event): """Display updated text.""" if self.text_layer: self.pyslip.DeleteLayer(self.text_layer) # convert values to sanity for layer attributes text = event.text font = event.font fontsize = event.fontsize textcolour = event.textcolour pointradius = event.pointradius pointcolour = event.pointcolour placement = event.placement if placement == 'none': placement = '' x = event.x if not x: x = 0 try: x = float(x) except ValueError: x = 0.0 y = event.y if not y: y = 0 try: y = float(y) except ValueError: y = 0.0 off_x = event.offset_x if not off_x: off_x = 0 try: off_x = int(off_x) except ValueError: x_off = 0 off_y = event.offset_y if not off_y: off_y = 0 try: off_y = int(off_y) except ValueError: off_y = 0 text_data = [(x, y, text, { 'placement': placement, 'radius': pointradius, 'fontname': font, 'fontsize': fontsize, 'colour': pointcolour, 'textcolour': textcolour, 'offset_x': off_x, 'offset_y': off_y })] self.text_layer = self.pyslip.AddTextLayer(text_data, map_rel=True, visible=True, name='<text_layer>') def textDelete(self, event): """Delete the text map-relative layer.""" if self.text_layer: self.pyslip.DeleteLayer(self.text_layer) self.text_layer = None ##### view-relative text layer def textViewUpdate(self, event): """Display updated text.""" if self.text_view_layer: self.pyslip.DeleteLayer(self.text_view_layer) # convert values to sanity for layer attributes text = event.text font = event.font fontsize = event.fontsize textcolour = event.textcolour pointradius = event.pointradius pointcolour = event.pointcolour placement = event.placement if placement == 'none': placement = '' x = event.x if not x: x = 0 x = int(x) y = event.y if not y: y = 0 y = int(y) off_x = event.offset_x if not off_x: off_x = 0 off_x = int(off_x) off_y = event.offset_y if not off_y: off_y = 0 off_y = int(off_y) # create a new text layer text_data = [(x, y, text, { 'placement': placement, 'radius': pointradius, 'fontname': font, 'fontsize': fontsize, 'colour': pointcolour, 'textcolour': textcolour, 'offset_x': off_x, 'offset_y': off_y })] self.text_view_layer = self.pyslip.AddTextLayer(text_data, map_rel=False, visible=True, name='<text_layer>') def textViewDelete(self, event): """Delete the text view-relative layer.""" if self.text_view_layer: self.pyslip.DeleteLayer(self.text_view_layer) self.text_view_layer = None def final_setup(self, level, position): """Perform final setup. level zoom level required position position to be in centre of view We do this in a CallAfter() function for those operations that must not be done while the GUI is "fluid". """ self.pyslip.GotoLevelAndPosition(level, position) ###### # Exception handlers ###### def handle_position_event(self, event): """Handle a pySlip POSITION event.""" posn_str = '' if event.mposn: (lon, lat) = event.mposn posn_str = ('%.*f / %.*f' % (LonLatPrecision, lon, LonLatPrecision, lat)) self.mouse_position.SetValue(posn_str) def handle_level_change(self, event): """Handle a pySlip LEVEL event.""" self.map_level.SetLabel('%d' % event.level)
def __init__(self, parent, title, filename='', placement=DefaultPlacement, pointradius=DefaultPointRadius, pointcolour=DefaultPointColour, x=0, y=0, offset_x=0, offset_y=0, **kwargs): """Initialise a LayerControl instance. parent reference to parent object title text to show in static box outline filename filename of image to show placement placement string for image pointradius radius of the image point pointcolour colour of the image point x, y X and Y coords offset_x X offset of image offset_y Y offset of image **kwargs keyword args for Panel """ # create and initialise the base panel wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY, **kwargs) self.SetBackgroundColour(wx.WHITE) self.v_filename = filename self.v_placement = placement self.v_pointradius = str(pointradius) self.v_pointcolour = pointcolour self.v_x = x self.v_y = y self.v_offset_x = offset_x self.v_offset_y = offset_y box = AppStaticBox(self, title) sbs = wx.StaticBoxSizer(box, orient=wx.VERTICAL) gbs = wx.GridBagSizer(vgap=10, hgap=10) # row 0 row = 0 gbs.Add(3, 1, (row, 0)) label = wx.StaticText(self, wx.ID_ANY, 'filename: ') gbs.Add(label, (row, 1), border=0, flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)) self.filename = ROTextCtrl(self, self.v_filename) gbs.Add(self.filename, (row, 2), span=(1, 3), border=0, flag=wx.EXPAND) gbs.Add(3, 1, (row, 5)) # row 1 row += 1 label = wx.StaticText(self, wx.ID_ANY, 'placement: ') gbs.Add(label, (row, 1), border=0, flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)) choices = [ 'nw', 'cn', 'ne', 'ce', 'se', 'cs', 'sw', 'cw', 'cc', 'none' ] style = wx.CB_DROPDOWN | wx.CB_READONLY self.placement = wx.ComboBox(self, value=self.v_placement, choices=choices, style=style) gbs.Add(self.placement, (row, 2), border=0) # row 2 row += 1 label = wx.StaticText(self, wx.ID_ANY, 'point radius: ') gbs.Add(label, (row, 1), border=0, flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)) style = wx.CB_DROPDOWN | wx.CB_READONLY self.pointradius = wx.ComboBox(self, value=self.v_pointradius, choices=PointRadiusChoices, style=style) gbs.Add(self.pointradius, (row, 2), border=0, flag=(wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)) label = wx.StaticText(self, wx.ID_ANY, 'point colour: ') gbs.Add(label, (row, 3), border=0, flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)) self.pointcolour = wx.Button(self, label='') self.pointcolour.SetBackgroundColour(self.v_pointcolour) gbs.Add(self.pointcolour, (row, 4), border=0, flag=wx.EXPAND) # row 3 row += 1 label = wx.StaticText(self, wx.ID_ANY, 'x: ') gbs.Add(label, (row, 1), border=0, flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)) self.x = wx.TextCtrl(self, value=str(self.v_x)) gbs.Add(self.x, (row, 2), border=0, flag=wx.EXPAND) label = wx.StaticText(self, wx.ID_ANY, 'y: ') gbs.Add(label, (row, 3), border=0, flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)) self.y = wx.TextCtrl(self, value=str(self.v_y)) gbs.Add(self.y, (row, 4), border=0, flag=wx.EXPAND) # row 4 row += 1 label = wx.StaticText(self, wx.ID_ANY, 'offset_x: ') gbs.Add(label, (row, 1), border=0, flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)) self.offset_x = wx.TextCtrl(self, value=str(self.v_offset_x)) gbs.Add(self.offset_x, (row, 2), border=0, flag=wx.EXPAND) label = wx.StaticText(self, wx.ID_ANY, ' offset_y: ') gbs.Add(label, (row, 3), border=0, flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)) self.offset_y = wx.TextCtrl(self, value=str(self.v_offset_y)) gbs.Add(self.offset_y, (row, 4), border=0, flag=wx.EXPAND) # row 5 row += 1 delete_button = wx.Button(self, label='Remove') gbs.Add(delete_button, (row, 2), border=10, flag=wx.EXPAND) update_button = wx.Button(self, label='Update') gbs.Add(update_button, (row, 4), border=10, flag=wx.EXPAND) sbs.Add(gbs) self.SetSizer(sbs) sbs.Fit(self) # row 6 (just a spacer) row += 1 gbs.Add(1, 3, (row, 0)) # bind events to handlers self.filename.Bind(wx.EVT_LEFT_UP, self.onFilename) self.pointcolour.Bind(wx.EVT_BUTTON, self.onPointColour) delete_button.Bind(wx.EVT_BUTTON, self.onDelete) update_button.Bind(wx.EVT_BUTTON, self.onUpdate)
class LayerControl(wx.Panel): def __init__(self, parent, title, filename='', placement=DefaultPlacement, pointradius=DefaultPointRadius, pointcolour=DefaultPointColour, x=0, y=0, offset_x=0, offset_y=0, **kwargs): """Initialise a LayerControl instance. parent reference to parent object title text to show in static box outline filename filename of image to show placement placement string for image pointradius radius of the image point pointcolour colour of the image point x, y X and Y coords offset_x X offset of image offset_y Y offset of image **kwargs keyword args for Panel """ # create and initialise the base panel wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY, **kwargs) self.SetBackgroundColour(wx.WHITE) self.v_filename = filename self.v_placement = placement self.v_pointradius = str(pointradius) self.v_pointcolour = pointcolour self.v_x = x self.v_y = y self.v_offset_x = offset_x self.v_offset_y = offset_y box = AppStaticBox(self, title) sbs = wx.StaticBoxSizer(box, orient=wx.VERTICAL) gbs = wx.GridBagSizer(vgap=10, hgap=10) # row 0 row = 0 gbs.Add(3, 1, (row, 0)) label = wx.StaticText(self, wx.ID_ANY, 'filename: ') gbs.Add(label, (row, 1), border=0, flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)) self.filename = ROTextCtrl(self, self.v_filename) gbs.Add(self.filename, (row, 2), span=(1, 3), border=0, flag=wx.EXPAND) gbs.Add(3, 1, (row, 5)) # row 1 row += 1 label = wx.StaticText(self, wx.ID_ANY, 'placement: ') gbs.Add(label, (row, 1), border=0, flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)) choices = [ 'nw', 'cn', 'ne', 'ce', 'se', 'cs', 'sw', 'cw', 'cc', 'none' ] style = wx.CB_DROPDOWN | wx.CB_READONLY self.placement = wx.ComboBox(self, value=self.v_placement, choices=choices, style=style) gbs.Add(self.placement, (row, 2), border=0) # row 2 row += 1 label = wx.StaticText(self, wx.ID_ANY, 'point radius: ') gbs.Add(label, (row, 1), border=0, flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)) style = wx.CB_DROPDOWN | wx.CB_READONLY self.pointradius = wx.ComboBox(self, value=self.v_pointradius, choices=PointRadiusChoices, style=style) gbs.Add(self.pointradius, (row, 2), border=0, flag=(wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)) label = wx.StaticText(self, wx.ID_ANY, 'point colour: ') gbs.Add(label, (row, 3), border=0, flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)) self.pointcolour = wx.Button(self, label='') self.pointcolour.SetBackgroundColour(self.v_pointcolour) gbs.Add(self.pointcolour, (row, 4), border=0, flag=wx.EXPAND) # row 3 row += 1 label = wx.StaticText(self, wx.ID_ANY, 'x: ') gbs.Add(label, (row, 1), border=0, flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)) self.x = wx.TextCtrl(self, value=str(self.v_x)) gbs.Add(self.x, (row, 2), border=0, flag=wx.EXPAND) label = wx.StaticText(self, wx.ID_ANY, 'y: ') gbs.Add(label, (row, 3), border=0, flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)) self.y = wx.TextCtrl(self, value=str(self.v_y)) gbs.Add(self.y, (row, 4), border=0, flag=wx.EXPAND) # row 4 row += 1 label = wx.StaticText(self, wx.ID_ANY, 'offset_x: ') gbs.Add(label, (row, 1), border=0, flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)) self.offset_x = wx.TextCtrl(self, value=str(self.v_offset_x)) gbs.Add(self.offset_x, (row, 2), border=0, flag=wx.EXPAND) label = wx.StaticText(self, wx.ID_ANY, ' offset_y: ') gbs.Add(label, (row, 3), border=0, flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT)) self.offset_y = wx.TextCtrl(self, value=str(self.v_offset_y)) gbs.Add(self.offset_y, (row, 4), border=0, flag=wx.EXPAND) # row 5 row += 1 delete_button = wx.Button(self, label='Remove') gbs.Add(delete_button, (row, 2), border=10, flag=wx.EXPAND) update_button = wx.Button(self, label='Update') gbs.Add(update_button, (row, 4), border=10, flag=wx.EXPAND) sbs.Add(gbs) self.SetSizer(sbs) sbs.Fit(self) # row 6 (just a spacer) row += 1 gbs.Add(1, 3, (row, 0)) # bind events to handlers self.filename.Bind(wx.EVT_LEFT_UP, self.onFilename) self.pointcolour.Bind(wx.EVT_BUTTON, self.onPointColour) delete_button.Bind(wx.EVT_BUTTON, self.onDelete) update_button.Bind(wx.EVT_BUTTON, self.onUpdate) def onFilename(self, event): """Change image filename.""" wildcard = ("PNG files (*.png)|*.png|" "JPG files (*.jpg)|*.jpg|" "All files (*.*)|*.*") filepath = None dialog = wx.FileDialog(None, "Choose an image file", os.getcwd(), "", wildcard, wx.FD_OPEN) if dialog.ShowModal() == wx.ID_OK: filepath = dialog.GetPath() dialog.Destroy() if filepath: self.filename.SetValue(filepath) def onPointColour(self, event): """Change text colour.""" colour = self.pointcolour.GetBackgroundColour() wxcolour = wx.ColourData() wxcolour.SetColour(colour) dialog = wx.ColourDialog(self, data=wxcolour) dialog.GetColourData().SetChooseFull(True) new_colour = None if dialog.ShowModal() == wx.ID_OK: new_colour = dialog.GetColourData().Colour dialog.Destroy() if new_colour: self.pointcolour.SetBackgroundColour(new_colour) def onDelete(self, event): """Remove image from map.""" event = LayerControlEvent(myEVT_DELETE, self.GetId()) self.GetEventHandler().ProcessEvent(event) def get_numeric_value(self, control): """Get numeric value of a textbox. control the textbox to query Return numeric value as a float. """ orig_value = control.GetValue() if not orig_value: value = '0' control.SetValue('0') else: value = orig_value try: value = float(value) except ValueError: return (orig_value, None) return (orig_value, value) def onUpdate(self, event): """Update image on map.""" # get x/y/offset_x/offset_y and check valid (orig_x, x) = self.get_numeric_value(self.x) (orig_y, y) = self.get_numeric_value(self.y) (orig_offset_x, offset_x) = self.get_numeric_value(self.offset_x) (orig_offset_y, offset_y) = self.get_numeric_value(self.offset_y) if (x is not None and y is not None and offset_x is not None and offset_y is not None): event = LayerControlEvent(myEVT_UPDATE, self.GetId()) event.filename = self.filename.GetValue() event.placement = self.placement.GetValue() event.radius = int(self.pointradius.GetValue()) event.colour = self.pointcolour.GetBackgroundColour() event.x = self.x.GetValue() event.y = self.y.GetValue() event.offset_x = self.offset_x.GetValue() event.offset_y = self.offset_y.GetValue() self.GetEventHandler().ProcessEvent(event) else: msg = 'These controls have bad values:\n' if x is None: name = ('x:' + ' ' * 20)[:12] msg += "\t%s\t%s\n" % (name, str(orig_x)) if y is None: name = ('y:' + ' ' * 20)[:12] msg += "\t%s\t%s\n" % (name, str(orig_y)) if offset_x is None: name = ('offset_x:' + ' ' * 20)[:12] msg += "\t%s\t%s\n" % (name, str(orig_offset_x)) if offset_y is None: name = ('offset_y:' + ' ' * 20)[:12] msg += "\t%s\t%s\n" % (name, str(orig_offset_y)) wx.MessageBox(msg, 'Warning', wx.OK | wx.ICON_ERROR)
class AppFrame(wx.Frame): def __init__(self): super().__init__(None, title='JC Geotag Tool') self.panel = wx.Panel(self, wx.ID_ANY) self.panel.SetBackgroundColour(wx.WHITE) self.panel.ClearBackground() all_display = wx.BoxSizer(wx.HORIZONTAL) self.panel.SetSizer(all_display) obj = __import__('pyslip', globals(), locals(), ['stamen_transport']) tileset = getattr(obj, 'stamen_transport') tile_obj = tileset.Tiles() # put map view in left of horizontal box self.pyslip = pyslip.pySlip(self.panel, tile_src=tile_obj, style=wx.SIMPLE_BORDER) all_display.Add(self.pyslip, proportion=1, flag=wx.EXPAND) # add controls at right controls = self.make_gui_controls(self.panel) all_display.Add(controls, proportion=0) self.panel.SetSizerAndFit(all_display) self.pyslip.Bind(pyslip.EVT_PYSLIP_LEVEL, self.level_change_event) self.pyslip.Bind(pyslip.EVT_PYSLIP_POSITION, self.mouse_posn_event) self.pyslip.Bind(wx.EVT_RIGHT_DOWN, self.right_click_event) original_img = '550D_2020-08-28_IMG_7103.jpg' thumb_img = '550D_2020-08-28_IMG_7103.jpg.thumb' img = Image.open(original_img) img.thumbnail((64, 64), Image.ANTIALIAS) img.save('550D_2020-08-28_IMG_7103.jpg.thumb', "JPEG") ShipImg = thumb_img ImageData = [ (120.0, 24.0, ShipImg, {'placement': 'cc'}), # Venus - 1826 (120.1, 24.1, ShipImg, {'placement': 'ne'}), # Wolverine - 1879 (120.2, 24.2, ShipImg, {'placement': 'nw'}), # Thomas Day - 1884 (120.3, 24.3, ShipImg, {'placement': 'sw'}), # Sybil - 1902 (120.4, 24.4, ShipImg, {'placement': 'se'}), # Prince of Denmark - 1863 (120.5, 24.5, ShipImg), # Moltke - 1911 (120.6, 24.6, ShipImg) ] MRShowLevels = [3, 4, 5, 6, 7, 8, 9, 10] self.image_layer = \ self.pyslip.AddImageLayer(ImageData, map_rel=True, visible=True, delta=10, show_levels=MRShowLevels, name='<image_layer>') self.point_layer = None self.pyslip.GotoLevelAndPosition(8, (118.01, 26.01)) self.last_clicked_loc = None self.Centre() self.Show() self.Maximize(True) def make_point_layer(self, long, lat): PointData = [(long, lat)] MRShowLevels = [3, 4, 5, 6, 7, 8, 9, 10] self.point_layer = \ self.pyslip.AddPointLayer(PointData, map_rel=True, colour='#ff0000f0', radius=6, # offset points to exercise placement offset_x=0, offset_y=0, visible=True, show_levels=MRShowLevels, delta=40, placement='nw', # check placement name='<pt_layer>') def make_gui_controls(self, parent): """Build the 'controls' part of the GUI parent reference to parent Returns reference to containing sizer object. Should really use GridBagSizer layout. """ # all controls in vertical box sizer controls = wx.BoxSizer(wx.VERTICAL) # put level and position into one 'controls' position tmp = wx.BoxSizer(wx.HORIZONTAL) tmp.AddSpacer(HorizSpacer) level = self.make_gui_level(parent) tmp.Add(level, proportion=0, flag=wx.EXPAND|wx.ALL) tmp.AddSpacer(HorizSpacer) mouse = self.make_gui_mouse(parent) tmp.Add(mouse, proportion=0, flag=wx.EXPAND|wx.ALL) tmp.AddSpacer(HorizSpacer) controls.Add(tmp, proportion=0, flag=wx.EXPAND|wx.ALL) controls.AddSpacer(VertSpacer) tmp = wx.BoxSizer(wx.HORIZONTAL) tmp.AddSpacer(HorizSpacer) self.file_list = wx.ListCtrl(parent, wx.ID_ANY, style = wx.LC_REPORT) self.file_list.InsertColumn(0, 'Path') tmp.Add(self.file_list, proportion=1, flag=wx.EXPAND|wx.ALL) tmp.AddSpacer(HorizSpacer) controls.Add(tmp, proportion=0, flag=wx.EXPAND|wx.ALL) controls.AddSpacer(VertSpacer) tmp = wx.BoxSizer(wx.HORIZONTAL) tmp.AddSpacer(HorizSpacer) open_file_btn = wx.Button(parent, wx.ID_ANY, 'Open') tmp.Add(open_file_btn, proportion=1, flag=wx.EXPAND|wx.ALL) tmp.AddSpacer(HorizSpacer) controls.Add(tmp, proportion=0, flag=wx.EXPAND|wx.ALL) controls.AddSpacer(VertSpacer) tmp = wx.BoxSizer(wx.HORIZONTAL) tmp.AddSpacer(HorizSpacer) set_location_btn = wx.Button(parent, wx.ID_ANY, 'Set Location') tmp.Add(set_location_btn, proportion=1, flag=wx.EXPAND|wx.ALL) tmp.AddSpacer(HorizSpacer) controls.Add(tmp, proportion=0, flag=wx.EXPAND|wx.ALL) controls.AddSpacer(VertSpacer) open_file_btn.Bind(wx.EVT_BUTTON, self.open_file_clicked) set_location_btn.Bind(wx.EVT_BUTTON, self.set_location_clicked) return controls def make_gui_level(self, parent): """Build the control that shows the level. parent reference to parent Returns reference to containing sizer object. """ # create objects txt = wx.StaticText(parent, wx.ID_ANY, 'Level: ') self.map_level = ROTextCtrl(parent, '', size=(30,-1), tooltip='Shows map zoom level') # lay out the controls sb = AppStaticBox(parent, 'Map level') box = wx.StaticBoxSizer(sb, orient=wx.HORIZONTAL) box.Add(txt, flag=(wx.ALIGN_CENTER_VERTICAL |wx.LEFT)) box.Add(self.map_level, proportion=0, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL) return box def make_gui_mouse(self, parent): """Build the mouse part of the controls part of GUI. parent reference to parent Returns reference to containing sizer object. """ # create objects txt = wx.StaticText(parent, wx.ID_ANY, 'Lon/Lat: ') self.mouse_position = ROTextCtrl(parent, '', size=(120,-1), tooltip=('Shows the mouse ' 'longitude and latitude ' 'on the map')) # lay out the controls sb = AppStaticBox(parent, 'Mouse position') box = wx.StaticBoxSizer(sb, orient=wx.HORIZONTAL) box.Add(txt, flag=(wx.ALIGN_CENTER_VERTICAL |wx.LEFT)) box.Add(self.mouse_position, proportion=0, flag=wx.RIGHT|wx.TOP|wx.BOTTOM) return box def mouse_posn_event(self, event): """Handle a "mouse position" event from the pySlipQt widget. The 'event' object has these attributes: event.etype the type of event event.mposn the new mouse position on the map (xgeo, ygeo) event.vposn the new mouse position on the view (x, y) """ if event.mposn: (lon, lat) = event.mposn # we clamp the lon/lat to zero here since we don't want small # negative values displaying as "-0.00" if abs(lon) < 0.01: lon = 0.0 if abs(lat) < 0.01: lat = 0.0 self.mouse_position.SetValue('%.2f/%.2f' % (lon, lat)) else: self.mouse_position.SetValue('') def level_change_event(self, event): self.map_level.SetValue(str(event.level)) def right_click_event(self, event): self.last_clicked_loc = self.pyslip.View2Geo(event.GetPosition()) if self.point_layer != None: self.pyslip.DeleteLayer(self.point_layer) self.make_point_layer(self.last_clicked_loc[0], self.last_clicked_loc[1]) def open_file_clicked(self, event): with wx.FileDialog(self, "Open files for geotagging", wildcard="Images (*.jpg)|*.jpg", style=wx.FD_OPEN | wx.FD_MULTIPLE) as fileDialog: if fileDialog.ShowModal() == wx.ID_CANCEL: return # the user changed their mind paths = fileDialog.GetPaths() for p in paths: self.file_list.InsertItem(0, p) def set_location_clicked(self, event): if self.last_clicked_loc == None: wx.MessageBox('Please right-click to set location first', 'Info', wx.OK | wx.ICON_INFORMATION) return for i in range(self.file_list.GetItemCount()): file_to_process = self.file_list.GetItemText(i) print(file_to_process) lng_deg = to_deg(self.last_clicked_loc[0], ["W", "E"]) lat_deg = to_deg(self.last_clicked_loc[1], ["S", "N"]) exiv_lng = (change_to_rational(lng_deg[0]), change_to_rational(lng_deg[1]), change_to_rational(lng_deg[2])) exiv_lat = (change_to_rational(lat_deg[0]), change_to_rational(lat_deg[1]), change_to_rational(lat_deg[2])) im = Image.open(file_to_process) exif_dict = piexif.load(im.info["exif"]) exif_dict["GPS"] = { piexif.GPSIFD.GPSVersionID: (2, 0, 0, 0), piexif.GPSIFD.GPSAltitudeRef: 0, piexif.GPSIFD.GPSAltitude: (1, 1), piexif.GPSIFD.GPSLatitudeRef: lat_deg[3], piexif.GPSIFD.GPSLatitude: exiv_lat, piexif.GPSIFD.GPSLongitudeRef: lng_deg[3], piexif.GPSIFD.GPSLongitude: exiv_lng, } exif_bytes = piexif.dump(exif_dict) piexif.insert(exif_bytes, file_to_process)