mercoledì 20 febbraio 2013

Chromix III - PanMenu and Go !

Segue dalla puntata precedente.

 

Un buon Menu


Siamo giunti agli ultimi passaggi del progetto Chromix e ci serve il Menu.

Ma Perché ci serve un Menu ?


Ci serve perché il Menu di LXDE così come la maggior parte dei Menu delle varie interfacce grafiche, è studiato per far funzionare un Desktop e attinge alle relative applicazioni, mentre il nostro obiettivo è creare qualcosa di semplice che si interfacci con le Web-App di Chromium.

Devo dire che dopo molte titubanze, tipo la ricerca di un plugin adatto o l'idea di scriverlo, mi è venuto in mente che in fondo la cosa più facile era fare uno  scriptino in Python.

Uno scriptino in Python ?  


Si uno scriptino che usasse Python e le GTK, con tanto di supporto CAIRO.

Per installare ciò che ci serve occorre usare il terminale di root cioè quello rosso e poi dare il comando :

apt-get install python-gtk

e per quanto riguarda le operazioni come root abbiamo finito.

Aperto un terminale normale, invece creiamo due folder nascosti come chromix nella nostra HOME :


mkdir .panmenu
mkdir .menu
cd .panmenu

E siamo dentro la neonata cartella dove dobbiamo scrivere (se vogliamo con leafpad) il primo file che si chiama config e che contiene la configurazione del pannello :

Potrebbe essere  :

bottom=50
minrows=3
maxrows=8
mincols=3
maxcols=12
Altra riga accettata da questo file è defaulticon che indica il nome dell'icona di default, la quale se non indicata diventa /.local/share/icons/default.png.

Nella stessa cartella dobbiamo inserire l'icona del programma che si chiama panmenu.png , io ho scelto questa :



La stessa icona la potete copiare con un :

mkdir ~/.local/share/icons
cp panmenu.png  ~/.local/share/icons/default.png

Oppure potete sceglierne una completamente diversa.

Ora aggiungiamo il link chiamato : panmenu.desktop e scriviamo le seguenti righe di testo :

[Desktop Entry]
Version=1.0
Name=PanMenu
Comment=MenuPanel
Exec=/home/chromix/.panmenu/panmenu
Terminal=false
X-MultipleArgs=false
Type=Application
Icon=/home/chromix/.panmenu/panmenu.png

Lo script


Dopo avere aggiunto le parti secondarie arriviamo ora al vero e proprio pannello in python-gtk il cui codice è questo :


#!/usr/bin/python
#
#  PANMENU
#  A simple menu panel for Linux written in Python GTK
#
#  Copyright 2013 by ElDuraMinga (elduraminga.blogspot.com)
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  See <http://www.gnu.org/licenses/>.
#

import pygtk
import gtk
import os
import fileinput
import gtk, gobject, cairo

global inside
inside=False

global page
page=0

global profile,home,folderlist

home=os.getenv("HOME")
folderlist=[]

global GLX,GLS
GLX=64
GLS=48

global bottom,minrows,mincols,maxrows,maxcols,defaulticon

bottom=0
minrows=5
mincols=3
maxrows=8
maxcols=8
defaulticon=home+"/.local/share/icons/default.png"


global nRows,nCols,nIndex,nCount
nRows=0
nCols=0
nIndex=-1
nCount=0

global pixmaps
pixmaps=[]


#Check single instance
def singleInstance() :
  if os.access("/run/lock/panmenu.lock",os.F_OK):
    quit()
  else :
    f=open("/run/lock/panmenu.lock","w")
    f.close()

def killInstance() :
  os.unlink("/run/lock/panmenu.lock")


# Read Global Profile
def profileRead() :
  global bottom
  global minrows
  global mincols
  global maxrows
  global maxcols
  global defaulticon
  global home
  for i in fileinput.input(home+"/.panmenu/config") :
    v=i.strip().split('=')
    if len(v)==2 :
      if (v[0]=='bottom') :
        bottom=int(v[1])
      if (v[0]=='minrows') :
        minrows=int(v[1])
      if (v[0]=='maxrows') :
        maxrows=int(v[1])
      if (v[0]=='mincols') :
        mincols=int(v[1])
      if (v[0]=='maxcols') :
        maxcols=int(v[1])
      if (v[0]=='defaulticon') :
        defaulticon=v[1]

def checkIcon( iconname ) :
    global defaulticon
    global home
    for dirname, dirnames, filenames in os.walk(home+"/.local/share/icons"):
      for i in filenames :
        if i[:len(iconname)]==iconname :
          return dirname+"/"+i
    return defaulticon



def parseApplicationFile( name ) :
  global folderlist
  fname=""
  ficon=""
  fexec=""
  for i in fileinput.input(name) :
    v=i.strip()
    if v[:5]=="Icon=" :
      if v[5]=='/' :
        ic=v[5:]
      else :
        ic=checkIcon(v[5:]+".png")
      ficon=ic
    if v[:5]=="Exec=" :
      fexec=v[5:]
    if v[:5]=="Name=" :
      fname=v[5:]
  folderlist.append([fname,ficon,fexec]) 
   

#Read the local applications directory
def applicationsRead() :
  global home
  dirlist=os.listdir(home+"/.menu")
  dirlist.sort()
  for i in dirlist :
    j=home+"/.menu/"+i
    if (os.path.isfile(j)) :
      if (i[-8:]==".desktop") :
        parseApplicationFile(j)
  dirlist=os.listdir(home+"/.local/share/applications")
  dirlist.sort() 
  for i in dirlist :
    j=home+"/.local/share/applications/"+i
    if (os.path.isfile(j)) :
      if (i[-8:]==".desktop") :
        parseApplicationFile(j)
       

def iconShow(surface,cr,x,y,w,h,alpha) :
    cr.save(); 
    width = surface.get_width()
    height = surface.get_height()   
    width=w/width;
    height=h/height;
    cr.scale(width, height);
    cr.set_source_surface(surface, x/width, y/height)
    if alpha :     
      cr.paint_with_alpha(0.5)
    else : 
      cr.paint();
    cr.restore();



# Create a GTK+ widget on which we will draw using Cairo
class Screen(gtk.DrawingArea):

    # Draw in response to an expose-event
    __gsignals__ = { "expose-event": "override" }

    # Handle the expose-event by drawing
    def do_expose_event(self, event):

        # Create the cairo context
        cr = self.window.cairo_create()

        # Restrict Cairo to the exposed area; avoid extra work
        cr.rectangle(event.area.x, event.area.y,
                event.area.width, event.area.height)
        cr.clip()

        self.draw(cr, *self.window.get_size())

    def draw(self, cr, width, height):
        global iconlist
        global nIndex
        j=page
        for i in range(nRows*nCols) :
          cx=GLX*((i%nCols)+1);
          cy=GLX*((i/nCols)+1);
          if (nIndex==j) :
            iconShow(folderlist[j][3],cr,float(cx-GLS/2),float(cy-GLS/2),float(GLS),float(GLS),True);
          else :
            iconShow(folderlist[j][3],cr,float(cx-GLS/2),float(cy-GLS/2),float(GLS),float(GLS),False);
          j+=1 
          if (j>=len(folderlist)) :
            break 
        if nIndex!=-1 : 
          cr.save(); 
          cr.set_source_rgb(0, 0, 0)
          
          cr.select_font_face("Nimbus Sans L", cairo.FONT_SLANT_NORMAL,cairo.FONT_WEIGHT_NORMAL)
          cr.set_font_size(20)
         
          cr.move_to(GLX/2, height-26)
          cr.show_text(folderlist[nIndex][0]) 
          cr.restore(); 
         

def motionEvent(widget, event):
  global folderlist
  global nIndex
  global inside
  inside=True
  j=page
  q=-1
  for i in range(nCols*nRows) :
    cx=GLX*((i%nCols)+1);
    cy=GLX*((i/nCols)+1);   
    if ( (event.x>=float(cx-GLX/2)) and (event.x<=float(cx+GLX/2)) and (event.y>=float(cy-GLX/2)) and (event.y<=float(cy+GLX/2)) ) :
      q=j
      break;
    j+=1
    if (j>=len(folderlist)) :
      break
  if (q!=nIndex) :
    nIndex=q
    widget.queue_draw();   
  return True

def buttonEvent(widget, event):
  global nIndex 
  global folderlist
  global page
  if event.type == gtk.gdk.BUTTON_PRESS  :
    if event.button == 1:
      killInstance()
      if (nIndex!=-1) :
        os.execl("/bin/sh","/bin/sh","-c",folderlist[nIndex][2]);
      gtk.main_quit()
    if event.button == 3:
      page+=nCols*nRows
      if page>=len(folderlist) :
        page=0
      widget.queue_draw()
  return True



def quitAll(widget, event):
  global inside
  if inside :
    killInstance()
    gtk.main_quit()
    return True
  
# Panel intialization and run
def run(Widget):
    global nRows
    global nCols
    global nIndex
    global nCount
    global folderlist
    global iconlist
   
    nRows=minrows
    nCols=mincols
    nCount=len(folderlist)
   
    while (nRows*nCols<nCount) :
      nRows+=1;
      if (nRows>maxrows) :
        nCols+=1;
        nRows=minrows
      if (nCols>maxcols) :
        nRows=maxrows
        nCols=maxcols
        nCount=nRows*nCols;
        break; 
       
       
    for i in folderlist :
      try:
        f = cairo.ImageSurface.create_from_png(i[1])
      except Exception, e:
        f = cairo.ImageSurface.create_from_png(defaulticon)
      i.append(f)
 
    window = gtk.Window()
    window.connect("delete-event", quitAll)
    widget = Widget()
    widget.show()
    window.add(widget)
   
   
    widget.connect("motion_notify_event", motionEvent)
    widget.connect("button_press_event", buttonEvent)
    widget.connect("leave_notify_event", quitAll)

    widget.set_events(gtk.gdk.EXPOSURE_MASK
                            | gtk.gdk.LEAVE_NOTIFY_MASK
                            | gtk.gdk.BUTTON_PRESS_MASK
                            | gtk.gdk.POINTER_MOTION_MASK
                            | gtk.gdk.POINTER_MOTION_HINT_MASK)
   
   
   
    color = gtk.gdk.Color(red=65535, green=65535, blue=65535, pixel=0)
    window.background=color
    window.resize(GLX*(nCols+1), GLX*(nRows+1)+32);
    window.move(5, gtk.gdk.screen_height() - bottom - 32 - GLX*(nRows+1));
    window.present()
    window.set_name ("menupanel")
    window.set_title("Menu Panel")
    window.set_decorated(False)
    gtk.main()
   
if __name__ == "__main__":
    singleInstance()
    try :
      profileRead()
      applicationsRead()
      run(Screen)
    except e:
      killInstance()



Lo dovete copia-incollare dentro un file chiamato panmenu nella stessa cartella, menù e renderlo eseguibile tipo :

chmod 755 panmenu


Come funziona PanMenu


Panmenu recupera le applicazioni dai desktop link contenuti in .menu, cioè quelle che aggiungeremo noi e .local/share/applications che invece sono quelle che aggiunge Chromium, ossia le web-app.

Entrambi andranno a reperire le icone relative in .local/share/icons . Notare che questo menù non usa le libmenu, ma fa per conto suo ed è estremamente dinamico.

Il suo funzionamento è la costruzione di una finestra a sinistra in basso BOTTOM righe sopra l'ultima linea, individuando la serie di icone del pannello mettendole in ordine alfabetico, infine visualizzando un rettangolo che si estende da MINROWS MINCOLS a MAXROWS MAXCOLS, in modo progressivo.

Oltre il termine di MAXROWS/MAXCOLS si procede a pagine e per andare sulle successive si usa il Right-Click del mouse a rotazione.

Il pannello scompare quando si esce dal menu col mouse o quando si preme su un link, nel qual caso viene usato un execl con sh per lanciare il comando, che resta così regolarmente "figlio" del pannello.

Nota : Se il menù non si apre più controllare ed eventualmente rimuovere il lock per l'istanza singola : /run/lock/panmenu.lock


Integrare PanMenu nel launcher di LXDE

 

Per prima cosa, con leafpad si va a modificare il file :

 ~/.config/lxpanel/LXDE/panels/panel

Aggiungendo al primo launcher una altro Button, col seguente codice :



...
Plugin {
    type = launchbar
    Config {
        Button {
            id=/home/chromix/.panmenu/panmenu.desktop
        }
        Button {
            id=/usr/share/applications/chromium.deskto       
        }
    }
}
...


poi dobbiamo fare in modo che l'applicazione non sia visualizzata nella taskbar, modificando opportunamente la configurazione di openbox cioè il file :

~/.config/openbox/lxde-rc.xml

aggiungendo in fondo, prima della chiusura del tag XML </applications> quattro righe:

...
  # end of the example
-->

    
 <application name="panmenu" >
   <skip_taskbar>yes</skip_taskbar>
   <skip_pager>yes</skip_pager>
 </application>

  </applications>
</openbox_config>
...

Ora non ci resta che popolare, con due applicazioni di base il .menu e per far questo diamo la sequenza dei comandi :

cd ~/.menu
cp /usr/share/applications/lxterminal.desktop .
cp /usr/share/applications/pcmanfm.desktop .
cd ~/.local/share/icons/
cp /usr/share/icons/nuoveXT2/48x48/apps/terminal.png lxterminal.png
cp /usr/share/icons/nuoveXT2/48x48/apps/file-manager.png system-file-manager.png
Cioè copiamo sia il link a queste due applicazioni (in .menu) che le relative icone (in .local/share/icons), come indicate all'interno dei link.

Abbiamo scelto icone 48x48 che è la dimensione standard per il menu.

Siccome non sono accettate le sostituzioni tipo %U nei links, andiamo nuovamente in .menu e sostituiamo la riga di exec di pcmanfm.desktop con :

...
Exec=pcmanfm ~/Downloads
...

A questo punto possiamo riavviare.

 Riavvio


Se avete fatto i regolari passaggi, cliccando sul nuovo launcher nel pannello del menù vedrete qualcosa tipo :






Facendo click sul file manager per altro, entreremo direttamente in Downloads, dove ci si aspetta che Chromium metta ciò che scarichiamo.

Inoltre sul tab sinistro di pcmanfm troviamo Applications , se clicchiamo qui, scopriamo che si tratta di un altro menu completo da cui possiamo avviare tutte le applicazioni installate :



dunque possiamo rimuovere quello sul pannello e lasciare solo il nostro PanMenu e Chromium.


 

 

Aggiungere Web-Apps e Links


Se apriamo Chromium sulla new-tab classica, dovremmo trovare l'elenco di tutte le applicazioni che abbiamo integrato.

All'inizio c'è solo il Chrome Web Store che però è un Web-App come tutte le altre dunque possiamo benissimo inserirlo come applicazione.

Per farlo clicchiamo col tasto destro sull'icona stessa e scegliamo Create Shortcuts :



Qui scegliamo di mettere il link nel menu e non sul desktop.



Ora, se andiamo a vedere il nostro menù, sarà diventato così :



E potremo lanciare l'applicazione come se fosse locale :




Come possiamo osservare nella finestra non c'è alcuna barra e l'icona nella taskbar non è quella di Chromium ma dello store !

Questo ovviamente, si può fare con tutte le applicazioni che vogliamo e ciò è abbastanza grandioso.

Ma c'è molto altro, tipo inserire un LINK come se fosse una web-app, con l'unico problema che l'icona sarà quella dello standard web di solito 16x16, poi scalata di tre volte.

Comunque apriamo Chromium , andiamo su un sito tipo www.twitter.com e clicchiamo sul menù di Chromium , sotto Tools troviamo Create Application Shortcut .



Stessa operazione di prima, cioè creazione del link non sul desktop ma nel menu ed ecco il nostro nuovo menu con l'uccellino.



 

Ed ecco il nostro Chromix



Abbiamo praticamente concluso il lavoro su Chromix.

Se volete, potete portare avanti piccole rifiniture, tipo cambiare la PNG che sta in /usr/share/lxde/images/logout-banner.png, con l'icona di Chromium 256x256, in modo da avere un menu di chiusura più raffinato e tante altre cose che lascio fare a voi.

Per ora arrivederci.






Nessun commento:

Posta un commento