diff options
author | Cyrille Bagard <nocbos@gmail.com> | 2015-09-01 21:36:22 (GMT) |
---|---|---|
committer | Cyrille Bagard <nocbos@gmail.com> | 2015-09-01 21:36:22 (GMT) |
commit | 29ddd755496589e7e1f9e38697e44d3cfe64df5e (patch) | |
tree | 3db093a9082276adb96a1ce67d175ade067c0788 /plugins/python/welcome | |
parent | e07a541d1dea13a19a587f2b97d12ed3443f235b (diff) |
Added a welcome panel as plugin using Python.
git-svn-id: svn://svn.gna.org/svn/chrysalide/trunk@573 abbe820e-26c8-41b2-8c08-b7b2b41f8b0a
Diffstat (limited to 'plugins/python/welcome')
-rw-r--r-- | plugins/python/welcome/Makefile.am | 14 | ||||
-rw-r--r-- | plugins/python/welcome/__init__.py | 4 | ||||
-rw-r--r-- | plugins/python/welcome/binary.py | 40 | ||||
-rw-r--r-- | plugins/python/welcome/board.py | 102 | ||||
-rw-r--r-- | plugins/python/welcome/panel.py | 147 | ||||
-rw-r--r-- | plugins/python/welcome/plugin.py | 36 | ||||
-rw-r--r-- | plugins/python/welcome/tip.py | 146 | ||||
-rw-r--r-- | plugins/python/welcome/tipoftheday.png | bin | 0 -> 10206 bytes | |||
-rw-r--r-- | plugins/python/welcome/tipoftheday.xcf | bin | 0 -> 17942 bytes | |||
-rw-r--r-- | plugins/python/welcome/version.py | 107 | ||||
-rw-r--r-- | plugins/python/welcome/website.py | 30 |
11 files changed, 626 insertions, 0 deletions
diff --git a/plugins/python/welcome/Makefile.am b/plugins/python/welcome/Makefile.am new file mode 100644 index 0000000..7dd16fa --- /dev/null +++ b/plugins/python/welcome/Makefile.am @@ -0,0 +1,14 @@ + +welcomedir = $(datadir)/openida/plugins/python/welcome + +welcome_DATA = \ + __init__.py \ + binary.py \ + board.py \ + panel.py \ + plugin.py \ + tip.py \ + version.py \ + website.py \ + tipoftheday.png \ + tipoftheday.xcf diff --git a/plugins/python/welcome/__init__.py b/plugins/python/welcome/__init__.py new file mode 100644 index 0000000..00a76af --- /dev/null +++ b/plugins/python/welcome/__init__.py @@ -0,0 +1,4 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +from welcome.plugin import WelcomePlugin as AutoLoad diff --git a/plugins/python/welcome/binary.py b/plugins/python/welcome/binary.py new file mode 100644 index 0000000..a9b508d --- /dev/null +++ b/plugins/python/welcome/binary.py @@ -0,0 +1,40 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +from welcome.board import SmallBoard +from gi.repository import Gtk, Gdk +import pychrysalide + + +class NewBinary(SmallBoard): + """Encourage l'ouverture d'un (premier) binaire.""" + + + def __init__(self): + """Construit le panneau avec son contenu.""" + + super(NewBinary, self).__init__() + + self._menu_item = pychrysalide.get_global_gobject('mnu_project_add_binary') + + msg = 'Analyse a new binary by clicking <a href="file:///#">here</a>.' + + desc = Gtk.Label(msg, use_markup=True, wrap=True) + desc.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK) + desc.set_track_visited_links(False) + desc.connect('activate-link', self.on_link_activated) + self.add(desc) + + + def on_link_activated(self, uri, data): + """Lance l'action associée au lien représenté.""" + + self._menu_item.activate() + + return True + + + def get_location(self): + """Fournit la localisation souhaitée pour la plache.""" + + return [ 0, 0, 1, 1 ] diff --git a/plugins/python/welcome/board.py b/plugins/python/welcome/board.py new file mode 100644 index 0000000..ebebbc3 --- /dev/null +++ b/plugins/python/welcome/board.py @@ -0,0 +1,102 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +from gi.repository import Gtk + + +class SmallBoard(Gtk.EventBox): + """Représente une tuile de support pour le panneau de bienvenue.""" + + + css = """ + +#classic { + + border-color: rgba(255, 255, 255, 0.2); + border-style: solid; + border-width: 1px; + +} + +#hover { + + border-color: rgba(255, 255, 255, 0.6); + border-style: solid; + border-width: 1px; + +} +""" + + + def __init__(self): + """Construit le panneau avec son contenu.""" + + super(SmallBoard, self).__init__() + + #self.set_size_request(250, 150) + self.set_has_window(True) + + self.props.opacity = 0.6 + + self._manager = None + + + def on_enter(self, widget, event): + """Réagit à un survol de la zone par la souris.""" + + self.set_name('hover') + + self._manager.define_selected_area(self.get_allocation()) + + self.props.opacity = 1.0 + + return False + + + def on_leave(self, widget, event): + """Réagit à une sortie de la zone par la souris.""" + + self.set_name('classic') + + if self._manager != None: + self._manager.define_selected_area(None) + + self.props.opacity = 0.6 + + return False + + + def get_location(self): + """Fournit la localisation souhaitée pour la plache.""" + + pass + + + def attach(self, manager): + """Lie partiellement la plache à son support et suit les survols.""" + + child = self.get_child() + + if child != None: + + child.props.margin = 20 + + self.set_name('classic') + + self._manager = manager + + + def track_children(widget, owner): + + widget.connect('enter-notify-event', self.on_enter) + widget.connect('leave-notify-event', self.on_leave) + + if isinstance(widget, Gtk.Container): + + children = widget.get_children() + + for child in children: + track_children(child, owner) + + + track_children(self, self) diff --git a/plugins/python/welcome/panel.py b/plugins/python/welcome/panel.py new file mode 100644 index 0000000..811f008 --- /dev/null +++ b/plugins/python/welcome/panel.py @@ -0,0 +1,147 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +from welcome.binary import NewBinary +from welcome.version import VersionChecker +from welcome.website import WebInvitation +from welcome.board import SmallBoard +from welcome.tip import TipOfTheDay +from gi.repository import GObject, Gtk, Gdk +from pychrysalide.gui.panels import PanelItem + + +class WelcomePanel(PanelItem): + """Display a welcome panel if nothing is open in the main part of the editor.""" + + + def __init__(self): + """Initialize the Python instance of the panel.""" + + content = self._build_panel_content() + + super(WelcomePanel, self).__init__('Welcome', 'First commands', content, 'M') + + + def _build_panel_content(self): + """Build content for the welcome panel.""" + + self._area = None + + # Constitution du support principal + + support = Gtk.Grid() + + support.props.halign = Gtk.Align.CENTER + support.props.valign = Gtk.Align.CENTER + support.props.margin = 20 + + support.set_column_homogeneous(True) + support.set_row_homogeneous(True) + support.set_column_spacing(20) + support.set_row_spacing(20) + + # Mise en place des différentes tuiles + + cells = { } + + for x in range(4): + for y in range(3): + cells[(x, y)] = False + + tiles = [ + + NewBinary(), + VersionChecker(), + WebInvitation(), + TipOfTheDay() + + ] + + for t in tiles: + + x, y, w, h = t.get_location() + + for i in range(x, x + w): + for j in range(y, y + h): + assert(cells[(i, j)] == False) + cells[(i, j)] = True + + t.attach(self) + support.attach(t, x, y, w, h) + + for x in range(4): + for y in range(3): + if not cells[(x, y)]: + tile = SmallBoard() + tile.attach(self) + support.attach(tile, x, y, 1, 1) + + # Charge les styles propres aux panneaux + + style_provider = Gtk.CssProvider() + + style_provider.load_from_data(SmallBoard.css.encode()) + + Gtk.StyleContext.add_provider_for_screen( + Gdk.Screen.get_default(), + style_provider, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION + ) + + + support.show_all() + return support + + + def define_selected_area(self, area): + + self._area = area + + #### + #self.queue_draw() + + + def draw_selected_area(self, widget, cr): + + + alloc = widget.get_allocation() + + + + + def draw_rounded2(cr, area, radius): + """ draws rectangles with rounded (circular arc) corners """ + from math import pi + a,b,c,d=area + cr.arc(a + radius, c + radius, radius, 2*(pi/2), 3*(pi/2)) + cr.arc(b - radius, c + radius, radius, 3*(pi/2), 4*(pi/2)) + cr.arc(b - radius, d - radius, radius, 0*(pi/2), 1*(pi/2)) # ;o) + cr.arc(a + radius, d - radius, radius, 1*(pi/2), 2*(pi/2)) + cr.rectangle(self._area.x, self._area.y, self._area.width, self._area.height) + cr.close_path() + cr.fill() + + + if self._area != None: + cr.set_source_rgba(0, 0, 0, 0.15) + + border = 4 + + pts = ( self._area.x - border, self._area.x + self._area.width + border, + self._area.y - border, self._area.y + self._area.height + border ) + + #cr.save() + + draw_rounded2(cr, pts, 16) + + ctx = self.get_style_context() + + + Gtk.render_background(ctx, cr, 0, 20, 100, 100) + Gtk.render_background(ctx, cr, self._area.x, self._area.y, self._area.width, self._area.height) + + + #cr.restore() + + + return False diff --git a/plugins/python/welcome/plugin.py b/plugins/python/welcome/plugin.py new file mode 100644 index 0000000..8a9f9a2 --- /dev/null +++ b/plugins/python/welcome/plugin.py @@ -0,0 +1,36 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +from pychrysalide import PluginModule +from welcome.panel import WelcomePanel + + +class WelcomePlugin(PluginModule): + """Interface graphique d'accueil.""" + + + def get_interface(self): + """Provide the full plugin description.""" + + desc = { + + 'name' : 'Welcome', + 'desc' : 'Introduce the software when no project is loaded', + 'version' : '0.1', + + 'actions' : [ PluginModule.PGA_PLUGIN_INIT ] + + } + + return desc + + + def init(self, ref): + """Initialise l'extension.""" + + self._panel = WelcomePanel() + + self._panel.dock() + self._panel.register() + + return True diff --git a/plugins/python/welcome/tip.py b/plugins/python/welcome/tip.py new file mode 100644 index 0000000..1a57575 --- /dev/null +++ b/plugins/python/welcome/tip.py @@ -0,0 +1,146 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +from welcome.board import SmallBoard +from gi.repository import Gtk +import random + +try: + import cairo + has_cairo = True + import os +except: + has_cairo = False + + +_tip_messages = [ + "Message 1", + "Message 2", + "Message 3", + "Message 4", + "Message 5", + "Message 6" +] + +assert(len(_tip_messages) > 0) + + +class TipOfTheDay(SmallBoard): + """Présente une série d'astuces du jour.""" + + + def __init__(self): + """Construit le panneau avec son contenu.""" + + super(TipOfTheDay, self).__init__() + + box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10) + self.add(box) + + # Barre de contrôle + + toolbar = Gtk.Box(spacing=10) + toolbar.props.margin = 20 + box.pack_end(toolbar, False, True, 0) + + btn = Gtk.Button('Previous') + btn.connect('clicked', self.show_previous_message) + toolbar.pack_start(btn, False, True, 8) + + btn = Gtk.Button('Next') + btn.connect('clicked', self.show_next_message) + toolbar.pack_start(btn, False, True, 8) + + # Contenu de l'astuce courante + + desc = Gtk.Label('', use_markup=True, wrap=True, xalign=0) + self._message = desc + desc.set_justify(Gtk.Justification.LEFT) + box.pack_end(desc, False, True, 0) + + # Titre + + desc = Gtk.Label("<b>Did you know?</b>", use_markup=True, wrap=True, xalign=0) + desc.set_justify(Gtk.Justification.LEFT) + box.pack_end(desc, False, True, 0) + + # Image de fond, si possible + + if has_cairo: + + os.chdir(os.path.dirname(__file__)) + + self._img = cairo.ImageSurface.create_from_png('tipoftheday.png') + + self.connect_after('draw', self.draw_background) + + # Sélection du message courant + + self._indexes = [i for i in range(len(_tip_messages))] + random.shuffle(self._indexes) + + self._index = 0 + + self.show_message() + + + def draw_background(self, widget, cr): + """Dessine une image de fond pour les conseils.""" + + img_width = self._img.get_width() + img_height = self._img.get_height() + + alloc = self.get_allocation() + target_height = alloc.height * 0.7 + + if img_height > target_height: + + scale = float(target_height) / float(img_height) + + cr.translate(alloc.width - img_width * scale - 10, 10) + cr.scale(scale, scale) + + else: + + cr.translate(alloc.width - img_width - 10, 10) + + cr.set_source_surface(self._img) + cr.paint_with_alpha(0.6) + + + def show_message(self): + """Affiche un conseil du jour donné.""" + + selected = self._indexes[self._index] + + msg = _tip_messages[selected] + + self._message.set_markup(msg) + + + def show_previous_message(self, button): + """Affiche un conseil du jour précédent.""" + + if self._index == 0: + self._index = len(_tip_messages) - 1 + else: + self._index = self._index - 1 + + self.show_message() + + + def show_next_message(self, button): + """Affiche un conseil du jour suivant.""" + + self._index = self._index + 1 + + if self._index == len(_tip_messages): + self._index = 0 + + self.show_message() + + + def get_location(self): + """Fournit la localisation souhaitée pour la plache.""" + + return [ 2, 1, 2, 2 ] diff --git a/plugins/python/welcome/tipoftheday.png b/plugins/python/welcome/tipoftheday.png Binary files differnew file mode 100644 index 0000000..12f017a --- /dev/null +++ b/plugins/python/welcome/tipoftheday.png diff --git a/plugins/python/welcome/tipoftheday.xcf b/plugins/python/welcome/tipoftheday.xcf Binary files differnew file mode 100644 index 0000000..dc9cf06 --- /dev/null +++ b/plugins/python/welcome/tipoftheday.xcf diff --git a/plugins/python/welcome/version.py b/plugins/python/welcome/version.py new file mode 100644 index 0000000..37ba001 --- /dev/null +++ b/plugins/python/welcome/version.py @@ -0,0 +1,107 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +from welcome.board import SmallBoard +from gi.repository import Gtk +import pychrysalide + +try: + import urllib3 + has_urllib3 = True +except: + has_urllib3 = False + + +class VersionChecker(SmallBoard): + """Affichage des versions courante et disponible, ainsi que des conclusions associées.""" + + + def __init__(self): + """Construit le panneau avec son contenu.""" + + super(VersionChecker, self).__init__() + + current = pychrysalide.revision() + + if has_urllib3: + + lastest = self.get_lastest_version('community') + up2date = self.get_update_status(current, lastest) + + if lastest != None and up2date != None: + + msg = '''Your version is: <b>%s</b> + +Lastest version is: <b>%s</b>''' + + caption = msg % (current, lastest if lastest != None else 'unknown') + + if up2date: + caption = caption + ''' + +Your software is <span color='green'><b>up-to-date</b></span>.''' + else: + caption = caption + ''' + +Your software is <b><span color='red'>outdated</span></b>.''' + + else: + + msg = '''Your version is: <b>%s</b> + +An error occurred while dealing with revision numbers.''' + + caption = msg % current + + else: + + msg = '''Your version is: <b>%s</b> + +To display the lastest available version, please install the <b>urllib3</b> package for Python.''' + + caption = msg % current + + desc = Gtk.Label(caption, use_markup=True, wrap=True) + desc.set_track_visited_links(False) + self.add(desc) + + + def get_lastest_version(self, category): + """Retrouve la dernière version disponible à partir du site officiel.""" + + lastest = None + + http = urllib3.PoolManager() + request = http.request('GET', 'http://localhost/mediawiki/data/versions') + + html = request.data.decode('utf-8') + + request.release_conn() + + available = html.split('\n') + + for a in available: + desc = a.split('=') + if desc[0] == category: + lastest = desc[1] + break + + return lastest + + + def get_update_status(self, current, lastest): + """Détermine le degré de mise à jour du système.""" + + if current[0] != 'r' or lastest[0] != 'r': + return None + + cur_rev = int(current[1:]) + last_rev = int(lastest[1:]) + + return (cur_rev >= last_rev) + + + def get_location(self): + """Fournit la localisation souhaitée pour la plache.""" + + return [ 2, 0, 1, 1 ] diff --git a/plugins/python/welcome/website.py b/plugins/python/welcome/website.py new file mode 100644 index 0000000..07c2016 --- /dev/null +++ b/plugins/python/welcome/website.py @@ -0,0 +1,30 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +from welcome.board import SmallBoard +from gi.repository import Gtk, Gdk + + +class WebInvitation(SmallBoard): + """Présente un lien rapide vers le site Web officiel.""" + + + def __init__(self): + """Construit le panneau avec son contenu.""" + + super(WebInvitation, self).__init__() + + msg = '''Get access to the online documentation and stay tuned by visiting the official website : + +<a href="http://0xdeadc0de.fr/chrysalide/">0xdeadc0de.fr/chrysalide</a>''' + + desc = Gtk.Label(msg, use_markup=True, wrap=True) + desc.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK) + desc.set_track_visited_links(False) + self.add(desc) + + + def get_location(self): + """Fournit la localisation souhaitée pour la plache.""" + + return [ 3, 0, 1, 1 ] |