[set_location] Refactor plugin

- Use new config dialog
- Fix broken input dialog
- Fix combobox selection
- Fix pylint warnings
This commit is contained in:
Daniel Brötzmann
2020-05-06 17:53:15 +02:00
parent 37ed0e86ae
commit 00602b52f1
2 changed files with 781 additions and 762 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -3,37 +3,43 @@ import time
import logging import logging
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from functools import partial
import gi import gi
from gi.repository import Gdk
from gi.repository import Gtk from gi.repository import Gtk
from nbxmpp.structs import LocationData from nbxmpp.structs import LocationData
from gajim.plugins.gui import GajimPluginConfigDialog
from gajim.plugins import GajimPlugin
from gajim.plugins.plugins_i18n import _
from gajim.common import app from gajim.common import app
from gajim.common import ged from gajim.common import ged
from gajim.common import helpers
from gajim.common import configpaths from gajim.common import configpaths
from gajim.common.helpers import sanitize_filename
from gajim.gtk.dialogs import InputDialog, WarningDialog from gajim.gtk.dialogs import DialogButton
from gajim.gtk.dialogs import InputDialog
from gajim.gtk.dialogs import WarningDialog
from gajim.plugins import GajimPlugin
from gajim.plugins.helpers import get_builder
from gajim.plugins.plugins_i18n import _
log = logging.getLogger('gajim.p.set_location') log = logging.getLogger('gajim.p.set_location')
CHAMPLAIN_AVAILABLE = True CHAMPLAIN_AVAILABLE = False
try: try:
gi.require_version('Clutter', '1.0') gi.require_version('Clutter', '1.0')
gi.require_version('GtkClutter', '1.0') gi.require_version('GtkClutter', '1.0')
gi.require_version('Champlain', '0.12') gi.require_version('Champlain', '0.12')
gi.require_version('GtkChamplain', '0.12') gi.require_version('GtkChamplain', '0.12')
from gi.repository import Clutter, GtkClutter from gi.repository import Clutter
GtkClutter.init([]) # Must be initialized before importing those: from gi.repository import GtkClutter
from gi.repository import Champlain, GtkChamplain GtkClutter.init([]) # Must be initialized before importing those:
except: from gi.repository import Champlain
log.exception('To view the map, you have to install all dependencies') from gi.repository import GtkChamplain
CHAMPLAIN_AVAILABLE = False CHAMPLAIN_AVAILABLE = True
except Exception:
log.info('To view the map, you have to install all dependencies')
class SetLocationPlugin(GajimPlugin): class SetLocationPlugin(GajimPlugin):
@@ -43,7 +49,7 @@ class SetLocationPlugin(GajimPlugin):
'or physical location. \nTo be able to set your location on the ' 'or physical location. \nTo be able to set your location on the '
'built-in map, you need to have gir1.2-gtkchamplain and ' 'built-in map, you need to have gir1.2-gtkchamplain and '
'gir1.2-gtkclutter-1.0 installed') 'gir1.2-gtkclutter-1.0 installed')
self.config_dialog = SetLocationPluginConfigDialog(self) self.config_dialog = partial(SetLocationConfigDialog, self)
self.config_default_values = { self.config_default_values = {
'alt': (1609, ''), 'alt': (1609, ''),
'area': ('Central Park', ''), 'area': ('Central Park', ''),
@@ -60,7 +66,7 @@ class SetLocationPlugin(GajimPlugin):
'room': ('Observatory', ''), 'room': ('Observatory', ''),
'street': ('34th and Broadway', ''), 'street': ('34th and Broadway', ''),
'text': ('Northwest corner of the lobby', ''), 'text': ('Northwest corner of the lobby', ''),
'uri': ('http://beta.plazes.com/plazes/1940:jabber_inc', ''), 'uri': ('', ''),
'presets': ({'default': {}}, ''), } 'presets': ({'default': {}}, ''), }
def activate(self): def activate(self):
@@ -102,49 +108,58 @@ class SetLocationPlugin(GajimPlugin):
LocationData(**data)) LocationData(**data))
class SetLocationPluginConfigDialog(GajimPluginConfigDialog): class SetLocationConfigDialog(Gtk.ApplicationWindow):
def init(self): def __init__(self, plugin, transient):
self.GTK_BUILDER_FILE_PATH = self.plugin.local_file_path( Gtk.ApplicationWindow.__init__(self)
'config_dialog.ui') self.set_application(app.app)
self.xml = Gtk.Builder() self.set_show_menubar(False)
self.xml.set_translation_domain('gajim_plugins') self.set_title(_('Set Location Configuration'))
self.xml.add_objects_from_file(self.GTK_BUILDER_FILE_PATH, self.set_transient_for(transient)
['config_box']) self.set_default_size(400, 600)
config_box = self.xml.get_object('config_box') self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
self.get_child().pack_start(config_box, True, True, 0) self.set_modal(True)
self.xml.connect_signals(self) self.set_destroy_with_parent(True)
self.connect('hide', self.on_hide)
self.connect('show', self.on_show) self._plugin = plugin
ui_path = Path(__file__).parent
self._ui = get_builder(ui_path.resolve() / 'config_dialog.ui')
self._ui.set_translation_domain('gajim_plugins')
self.add(self._ui.config_box)
self.show_all()
self._ui.connect_signals(self)
self.connect('hide', self._on_hide)
self.connect('show', self._on_show)
self.is_active = None self.is_active = None
self._initialize()
self.preset_combo = self.xml.get_object('preset_combobox') def _initialize(self):
self.preset_liststore = Gtk.ListStore(str) self._preset_liststore = Gtk.ListStore(str)
self.preset_combo.set_model(self.preset_liststore) self._ui.preset_combobox.set_model(self._preset_liststore)
cellrenderer = Gtk.CellRendererText() cellrenderer = Gtk.CellRendererText()
self.preset_combo.pack_start(cellrenderer, True) self._ui.preset_combobox.pack_start(cellrenderer, True)
self.preset_combo.add_attribute(cellrenderer, 'text', 0) self._ui.preset_combobox.add_attribute(cellrenderer, 'text', 0)
def on_run(self):
if not self.is_active: if not self.is_active:
pres_keys = sorted(self.plugin.config['presets'].keys()) pres_keys = sorted(self._plugin.config['presets'].keys())
for key in pres_keys: for key in pres_keys:
self.preset_liststore.append((key,)) self._preset_liststore.append((key,))
self._ui.preset_combobox.set_active(0)
for name in self.plugin.config_default_values: for name in self._plugin.config_default_values:
if name == 'presets': if name == 'presets':
continue continue
widget = self.xml.get_object(name) widget = self._ui.get_object(name)
widget.set_text(str(self.plugin.config[name])) widget.set_text(str(self._plugin.config[name]))
map_placeholder = self.xml.get_object('map_placeholder')
dependency_bar = self.xml.get_object('dependency_warning')
if CHAMPLAIN_AVAILABLE and not self.is_active: if CHAMPLAIN_AVAILABLE and not self.is_active:
map_placeholder.set_no_show_all(True) self._ui.map_placeholder.set_no_show_all(True)
map_placeholder.hide() self._ui.map_placeholder.hide()
dependency_bar.hide() self._ui.dependency_warning.hide()
map_box = self.xml.get_object('map_box') self._ui.map_box.set_size_request(400, -1)
map_box.set_size_request(400, -1)
embed = GtkChamplain.Embed() embed = GtkChamplain.Embed()
@@ -152,29 +167,32 @@ class SetLocationPluginConfigDialog(GajimPluginConfigDialog):
self.view.set_reactive(True) self.view.set_reactive(True)
self.view.set_property('kinetic-mode', True) self.view.set_property('kinetic-mode', True)
self.view.set_property('zoom-level', 12) self.view.set_property('zoom-level', 12)
self.view.connect('button-release-event', self.map_clicked, self.view.connect('button-release-event', self._map_clicked,
self.view) self.view)
scale = Champlain.Scale() scale = Champlain.Scale()
scale.connect_view(self.view) scale.connect_view(self.view)
self.view.add_child(scale) self.view.add_child(scale)
lat = self.plugin.config['lat'] lat = self._plugin.config['lat']
lon = self.plugin.config['lon'] lon = self._plugin.config['lon']
if not self.is_valid_coord(lat, lon): if not self._is_valid_coord(lat, lon):
self.lat = self.lon = 0.0 self.lat = self.lon = 0.0
self.xml.get_object('lat').set_text('0.0') self._ui.lat.set_text('0.0')
self.xml.get_object('lon').set_text('0.0') self._ui.lon.set_text('0.0')
self.view.center_on(self.lat, self.lon) self.view.center_on(self.lat, self.lon)
icon = 'org.gajim.Gajim.svg' icon = 'org.gajim.Gajim.svg'
icons_dir = Path(configpaths.get('ICONS')) / 'hicolor/scalable/apps' icons_dir = Path(configpaths.get('ICONS')) / 'hicolor/scalable/apps'
self.path_to_image = icons_dir / icon self.path_to_image = icons_dir / icon
map_box.pack_start(embed, expand=True, fill=True, padding=0) self._ui.map_box.pack_start(
embed, expand=True, fill=True, padding=0)
self._ui.map_box.show_all()
self.is_active = True self.is_active = True
self.layer = Champlain.MarkerLayer() self.layer = Champlain.MarkerLayer()
texture = Clutter.Texture() texture = Clutter.Texture()
texture.set_from_file(str(self.path_to_image)) texture.set_from_file(str(self.path_to_image))
texture.set_size(32, 32) texture.set_size(32, 32)
@@ -184,44 +202,45 @@ class SetLocationPluginConfigDialog(GajimPluginConfigDialog):
self.view.add_layer(self.layer) self.view.add_layer(self.layer)
self.layer.add_marker(self.marker) self.layer.add_marker(self.marker)
self.markers_is_visible = False self.markers_is_visible = False
self.xml.get_object('lat').connect('changed', self.on_latlon_changed) self._ui.lat.connect('changed', self._on_latlon_changed)
self.xml.get_object('lon').connect('changed', self.on_latlon_changed) self._ui.lon.connect('changed', self._on_latlon_changed)
self.layer.animate_in_all_markers() self.layer.animate_in_all_markers()
self.contacts_layer = Champlain.MarkerLayer() self.contacts_layer = Champlain.MarkerLayer()
def on_show(self, widget): def _on_show(self, _widget):
if CHAMPLAIN_AVAILABLE: if CHAMPLAIN_AVAILABLE:
self.contacts_layer.remove_all() self.contacts_layer.remove_all()
self.view.center_on(self.lat, self.lon) self.view.center_on(self.lat, self.lon)
self.show_contacts() self._show_contacts()
def on_hide(self, widget): def _on_hide(self, widget):
for name in self.plugin.config_default_values: for name in self._plugin.config_default_values:
if name in ['presets', 'lat', 'lon']: if name in ['presets', 'lat', 'lon']:
continue continue
widget = self.xml.get_object(name) widget = self._ui.get_object(name)
self.plugin.config[name] = widget.get_text() self._plugin.config[name] = widget.get_text()
lat = self.xml.get_object('lat').get_text() lat = self._ui.lat.get_text()
lon = self.xml.get_object('lon').get_text() lon = self._ui.lon.get_text()
if self.is_valid_coord(lat, lon): if self._is_valid_coord(lat, lon):
self.plugin.config['lat'] = lat self._plugin.config['lat'] = lat
self.plugin.config['lon'] = lon self._plugin.config['lon'] = lon
if self.plugin.active: if self._plugin.active:
self.plugin.activate() self._plugin.activate()
else: else:
self.plugin.config['lat'] = '0.0' self._plugin.config['lat'] = '0.0'
self.plugin.config['lon'] = '0.0' self._plugin.config['lon'] = '0.0'
error_text = _('Latitude or Longitude field contains an invalid value') error_text = _('Latitude or Longitude field contains an invalid '
'value')
WarningDialog(_('Wrong coordinates'), error_text, self) WarningDialog(_('Wrong coordinates'), error_text, self)
def map_clicked(self, actor, event, view): def _map_clicked(self, _actor, event, view):
x, y = event.x, event.y x_coord, y_coord = event.x, event.y
lat, lon = view.x_to_longitude(x), view.y_to_latitude(y) lat, lon = view.x_to_longitude(x_coord), view.y_to_latitude(y_coord)
if event.button == 3: if event.button == 3:
self.marker.set_location(lat, lon) self.marker.set_location(lat, lon)
self.xml.get_object('lon').set_text(str(lat)) self._ui.lon.set_text(str(lat))
self.xml.get_object('lat').set_text(str(lon)) self._ui.lat.set_text(str(lon))
if event.button == 2: if event.button == 2:
if self.markers_is_visible: if self.markers_is_visible:
self.contacts_layer.animate_out_all_markers() self.contacts_layer.animate_out_all_markers()
@@ -229,24 +248,24 @@ class SetLocationPluginConfigDialog(GajimPluginConfigDialog):
self.contacts_layer.animate_in_all_markers() self.contacts_layer.animate_in_all_markers()
self.markers_is_visible = not self.markers_is_visible self.markers_is_visible = not self.markers_is_visible
def is_valid_coord(self, lat, lon): def _is_valid_coord(self, lat, lon):
try: try:
self.lat = float(lat) self.lat = float(lat)
self.lon = float(lon) self.lon = float(lon)
except ValueError as e: except ValueError:
return return
if not -85 < self.lat < 85 or not -180 < self.lon < 180: if not -85 < self.lat < 85 or not -180 < self.lon < 180:
return return
return True return True
def on_latlon_changed(self, widget): def _on_latlon_changed(self, _widget):
lat = self.xml.get_object('lat').get_text() lat = self._ui.lat.get_text()
lon = self.xml.get_object('lon').get_text() lon = self._ui.lon.get_text()
if self.is_valid_coord(lat, lon): if self._is_valid_coord(lat, lon):
self.marker.set_location(self.lat, self.lon) self.marker.set_location(self.lat, self.lon)
self.view.go_to(self.lat, self.lon) self.view.go_to(self.lat, self.lon)
def show_contacts(self): def _show_contacts(self):
data = {} data = {}
accounts = app.contacts._accounts accounts = app.contacts._accounts
for account in accounts: for account in accounts:
@@ -266,8 +285,8 @@ class SetLocationPluginConfigDialog(GajimPluginConfigDialog):
self.contacts_layer = Champlain.MarkerLayer() self.contacts_layer = Champlain.MarkerLayer()
for jid in data: for jid in data:
path = self.get_path_to_generic_or_avatar(self.path_to_image, path = self._get_path_to_generic_or_avatar(
jid=jid, suffix='') self.path_to_image, jid=jid, suffix='')
texture = Clutter.Texture() texture = Clutter.Texture()
texture.set_from_file(path) texture.set_from_file(path)
texture.set_size(32,32) texture.set_size(32,32)
@@ -280,7 +299,8 @@ class SetLocationPluginConfigDialog(GajimPluginConfigDialog):
self.contacts_layer.animate_in_all_markers() self.contacts_layer.animate_in_all_markers()
self.markers_is_visible = True self.markers_is_visible = True
def get_path_to_generic_or_avatar(self, generic, jid=None, suffix=None): @staticmethod
def _get_path_to_generic_or_avatar(generic, jid=None, suffix=None):
""" """
Choose between avatar image and default image Choose between avatar image and default image
@@ -289,7 +309,7 @@ class SetLocationPluginConfigDialog(GajimPluginConfigDialog):
""" """
if jid: if jid:
# we want an avatar # we want an avatar
puny_jid = helpers.sanitize_filename(jid) puny_jid = sanitize_filename(jid)
path_to_file = os.path.join( path_to_file = os.path.join(
configpaths.get('AVATAR'), puny_jid) + suffix configpaths.get('AVATAR'), puny_jid) + suffix
path_to_local_file = path_to_file + '_local' path_to_local_file = path_to_file + '_local'
@@ -303,45 +323,56 @@ class SetLocationPluginConfigDialog(GajimPluginConfigDialog):
return path_to_file_full return path_to_file_full
return os.path.abspath(generic) return os.path.abspath(generic)
def on_preset_button_clicked(self, widget): def _on_preset_button_clicked(self, _widget):
def on_ok(preset_name): def _on_save(preset_name):
if preset_name == '': if preset_name == '':
return return
preset = {} preset = {}
for name in self.plugin.config_default_values: for name in self._plugin.config_default_values:
if name == 'presets': if name == 'presets':
continue continue
widget = self.xml.get_object(name) widget = self._ui.get_object(name)
preset[name] = widget.get_text() preset[name] = widget.get_text()
preset = {preset_name: preset} preset = {preset_name: preset}
presets = dict(list(self.plugin.config['presets'].items()) + \ presets = dict(list(
list(preset.items())) self._plugin.config['presets'].items()) + list(preset.items()))
if preset_name not in list(self.plugin.config['presets'].keys()): if preset_name not in list(self._plugin.config['presets'].keys()):
iter_ = self.preset_liststore.append((preset_name,)) iter_ = self._preset_liststore.append((preset_name,))
self.plugin.config['presets'] = presets self._ui.preset_combobox.set_active_iter(iter_)
self.set_modal(False) self._plugin.config['presets'] = presets
active_preset = self._ui.preset_combobox.get_active()
current_preset = self._preset_liststore[active_preset][0]
InputDialog(_('Save as Preset'), InputDialog(_('Save as Preset'),
_('Please type a name for this preset'), _('Save as Preset'),
'default', is_modal=True, ok_handler=on_ok) _('Please enter a name for this preset'),
[DialogButton.make('Cancel'),
DialogButton.make('Accept',
text=_('Save'),
callback=_on_save)],
input_str=current_preset).show()
def on_preset_combobox_changed(self, widget): def _on_preset_combobox_changed(self, widget):
model = widget.get_model() active = widget.get_active_iter()
active = widget.get_active() if active is None:
if active < 0: self._ui.del_preset.set_sensitive(False)
self.xml.get_object('del_preset').set_sensitive(False)
return return
pres_name = model[active][0]
for name in list(self.plugin.config['presets'][pres_name].keys()):
widget = self.xml.get_object(name)
widget.set_text(str(self.plugin.config['presets'][pres_name][name]))
self.xml.get_object('del_preset').set_sensitive(True) pres_name = self._preset_liststore[active][0]
self._ui.del_preset.set_sensitive(pres_name != 'default')
for name in list(self._plugin.config['presets'][pres_name].keys()):
widget = self._ui.get_object(name)
widget.set_text(str(self._plugin.config['presets'][pres_name][name]))
def on_del_preset_clicked(self, widget): def _on_del_preset_clicked(self, _widget):
active = self.preset_combo.get_active() active = self._ui.preset_combobox.get_active()
active_iter = self.preset_combo.get_active_iter() active_iter = self._ui.preset_combobox.get_active_iter()
name = self.preset_liststore[active][0] name = self._preset_liststore[active][0]
presets = self.plugin.config['presets'] if name == 'default':
return
presets = self._plugin.config['presets']
del presets[name] del presets[name]
self.plugin.config['presets'] = presets self._plugin.config['presets'] = presets
self.preset_liststore.remove(active_iter) self._preset_liststore.remove(active_iter)
self._ui.preset_combobox.set_active(0)