user@linuxtrack:~ $ python -c 'print("Soyez les bienvenus !")'

Vous n'êtes pas identifié(e).

#1 09-10-2017 14:59:10

Mr. S.
Membre

Portage du paquet debian stretch "cyclope" vers fedora 26

Portage du paquet debian stretch "cyclope" vers fedora 26
Sommaire :

Introduction
1 / Echec de dpkg-repack sous KissOs-7.0
2 / téléchargement du paquet cyclope (pour debian) depuis le dépôt handylinux
3 /  Création d'un dossier de travail
4 / Echec de l’utilisation du paquet alien pour transformer le paquet cyclope (pour debian) en paquet pour fedora
5 / Procédure d’extraction du paquet cyclope (pour debian)
6 / Lancement du  script python dans l’environnement linux (cela marche également quelque soit l’unix utilisé, linux, BSD ...)
7 / Manuel d’utilisation du script cyclope
8 / Script cyclope à lancer avec python
Conclusion
Sources


Introduction

Il s’agit ici non seulement de vous présenter le paquet cyclope, visionneuse photo ultra-légère, conçu par Thuban) mais encore de vous permettre de l’utiliser sur n’importe quelle système unix [linux (Arch, debian, fedora, gentoo, void, …) ou BSD (Net, Open …) et que sais-je encore !

Ma plate-forme de travail a autant été :

- un acer aspire 9410 sous KissOs-0.7 mono-boot :
- un lenovo G50-45 UEFI SECURE BOOT sous Viperr Fedora remix 9 fc 26, dopée aux kickstarts fedora labs (https://github.com/SamsungARTIK/fedora-spin-kickstarts). D’abord dual boot avec Windows 8/10, puis mono boot (mais toujours UEFI SECURE BOOT!) car j’en avais marre des mises à jour longues de Windows dont je ne me sers plus à la maison depuis 2010 environ.


1 / Echec de dpkg-repack sous KissOs-0.7
stevie@kiss0s ~
$ dpkg-repack cyclope
dpkg-repack: Fatal Error: This program should be run as root (or you could use fakeroot -u). Aborting.
stevie@kiss0s ~
$ fakeroot -u
bash: fakeroot : commande introuvable
stevie@kiss0s ~
$ sudo apt-get -y install fakeroot
[sudo] Mot de passe de stevie : 
Lecture des listes de paquets... Fait
Construction de l'arbre des dépendances 
Lecture des informations d'état... Fait
The following additional packages will be installed:
libfakeroot
Les NOUVEAUX paquets suivants seront installés :
fakeroot libfakeroot
0 mis à jour, 2 nouvellement installés, 0 à enlever et 1 non mis à jour.
Il est nécessaire de prendre 134 ko dans les archives.
Après cette opération, 369 ko d'espace disque supplémentaires seront utilisés.
Réception de:1 http://deb.debian.org/debian stretch/main i386 libfakeroot i386 1.21-3.1 [46,8 kB]
Réception de:2 http://deb.debian.org/debian stretch/main i386 fakeroot i386 1.21-3.1 [86,8 kB]
134 ko réceptionnés en 0s (231 ko/s)
Sélection du paquet libfakeroot:i386 précédemment désélectionné.
(Lecture de la base de données... 80611 fichiers et répertoires déjà installés.)
Préparation du dépaquetage de .../libfakeroot_1.21-3.1_i386.deb ...
Dépaquetage de libfakeroot:i386 (1.21-3.1) ...
Sélection du paquet fakeroot précédemment désélectionné.
Préparation du dépaquetage de .../fakeroot_1.21-3.1_i386.deb ...
Dépaquetage de fakeroot (1.21-3.1) ...
Traitement des actions différées (« triggers ») pour libc-bin (2.24-11+deb9u1) ...
Paramétrage de libfakeroot:i386 (1.21-3.1) ...
Traitement des actions différées (« triggers ») pour man-db (2.7.6.1-2) ...
Paramétrage de fakeroot (1.21-3.1) ...
update-alternatives: utilisation de « /usr/bin/fakeroot-sysv » pour fournir « /usr/bin/fakeroot » (fakeroot) en mode automatique
Traitement des actions différées (« triggers ») pour libc-bin (2.24-11+deb9u1) ...
stevie@kiss0s ~
$ fakeroot -u
root@kiss0s ~
$ sudo dpkg-repack cyclope
ERROR: ld.so: object 'libfakeroot-sysv.so' from LD_PRELOAD cannot be preloaded (cannot open shared object file): ignored.
dpkg-query: le paquet « cyclope » n'est pas installé et aucune information n'est disponible
Utilisez dpkg --info (= dpkg-deb --info) pour examiner les fichiers
archives, et dpkg --contents (= dpkg-deb --contents) pour afficher leur
contenu.
Use of uninitialized value in pattern match (m//) at /usr/bin/dpkg-repack line 138.
Use of uninitialized value in concatenation (.) or string at /usr/bin/dpkg-repack line 139.
dpkg-repack: Fatal Error: Package cyclope not fully installed: 
root@kiss0s ~
$ exit
exit
stevie@kiss0s ~
$ fakeroot -u
root@kiss0s ~
$ dpkg-repack cyclope
dpkg-query: le paquet « cyclope » n'est pas installé et aucune information n'est disponible
Utilisez dpkg --info (= dpkg-deb --info) pour examiner les fichiers
archives, et dpkg --contents (= dpkg-deb --contents) pour afficher leur
contenu.
Use of uninitialized value in pattern match (m//) at /usr/bin/dpkg-repack line 138.
Use of uninitialized value in concatenation (.) or string at /usr/bin/dpkg-repack line 139.
dpkg-repack: Fatal Error: Package cyclope not fully installed: 
root@kiss0s ~
$
2 / Téléchargement du paquet cyclope (pour debian) depuis le dépôt handylinux :

En partant du sources.list d'Handylinux (http://repo.handylinux.org/handylinux.list) :

 ## HandyLinux v1 ##
deb http://handylinux.org/repo/debian stable main
#deb-src http://handylinux.org/repo/debian stable main

## HandyLinux v1 Compiz ##
deb http://handylinux.org/repo/debian compiz main
#deb-src http://handylinux.org/repo/debian compiz main

## HandyLinux v2 Jessie ##
deb http://handylinux.org/repo/debian jessie main
#deb-src http://handylinux.org/repo/debian jessie main

## HandyLinux Testing ##
deb http://handylinux.org/repo/debian testing main
#deb-src http://handylinux.org/repo/debian testing main

=> Extraire la "bonne adresse", soit "http://handylinux.org/repo/debian/".

Nous obtenons ceci :

Index of /repo/debian

Icon  Name                    Last modified      Size  Description[DIR] Parent Directory                             -   
[DIR] conf/                   06-Jun-2017 18:58    -   
[DIR] db/                     06-Jun-2017 18:58    -   
[DIR] dists/                  06-Jun-2017 18:58    -   
[DIR] lists/                  06-Jun-2017 18:58    -   
[DIR] pool/                   06-Jun-2017 18:58

=> Se rendre dans le dossier "pool" :

=> Naviguer dans le dépôt jusqu'à l'url "http://handylinux.org/repo/debian/pool/main/c/"

Index of /repo/debian/pool/main/c

Icon  Name                    Last modified      Size  Description[DIR] Parent Directory                             -   
[DIR] ccsm/                   06-Jun-2017 18:59    -   
[DIR] compiz-deskmenu/        06-Jun-2017 18:59    -   
[DIR] compiz-fusion-plugin..> 06-Jun-2017 19:02    -   
[DIR] compiz-fusion-plugin..> 06-Jun-2017 19:00    -   
[DIR] compiz/                 06-Jun-2017 19:01    -   
[DIR] compizconfig-python/    06-Jun-2017 18:59    -   
[DIR] copcoll/                06-Jun-2017 18:59    -   
[DIR] cyclope/                06-Jun-2017 19:01 

=> Entrer dans cyclope : url "http://handylinux.org/repo/debian/pool/main/c/cyclope"

Index of /repo/debian/pool/main/c/cyclope

Icon  Name                    Last modified      Size  Description[DIR] Parent Directory                             -   
[   ] cyclope_1.5-5.debian..> 06-Jun-2017 19:01  5.1K  
[TXT] cyclope_1.5-5.dsc       06-Jun-2017 19:01  841   
[   ] cyclope_1.5-5_all.deb   06-Jun-2017 19:01   20K  
[   ] cyclope_1.5.orig.tar.xz 06-Jun-2017 19:01  201K
3 /  Création d'un dossier de travail :
$ mkdir /home/$user/CYCLOPE

=> Télécharger le fichier "cyclope_1.5-5_all.deb" par un clic droit souris

stevie ~ CYCLOPE dir
cyclope_1.5-5_all.deb
 stevie ~ CYCLOPE ls
cyclope_1.5-5_all.deb

=> les commandes ls et dir fonctionnent sous debian et sous fedora pour déterminer le contenu d'un dossier.


4 / Echec de l’utilisation du paquet alien pour transformer le paquet cyclope (pour debian) en paquet pour fedora :

stevie  ~  CYCLOPE  su
Mot de passe : 
[root@Host-002 CYCLOPE]# alien -r *.deb
cyclope-1.5-6.noarch.rpm generated
[root@Host-002 CYCLOPE]# rpm -ivh *.rpm
Préparation...                       ################################# [100%]
	le fichier /usr/bin de l'installation de cyclope-1.5-6.noarch entre en conflit avec le fichier du paquet viperr-obkey-8.0-viperr.fc24.noarch
	le fichier /usr/bin de l'installation de cyclope-1.5-6.noarch entre en conflit avec le fichier du paquet viperrconf-9.7-viperr.fc24.noarch
	le fichier /usr/bin de l'installation de cyclope-1.5-6.noarch entre en conflit avec le fichier du paquet viperr-conposite-8.0-viperr.fc24.noarch
	le fichier / de l'installation de cyclope-1.5-6.noarch entre en conflit avec le fichier du paquet filesystem-3.2-40.fc26.x86_64
	le fichier /usr/bin de l'installation de cyclope-1.5-6.noarch entre en conflit avec le fichier du paquet filesystem-3.2-40.fc26.x86_64

=> Le paquet cyclope alienisé en version fedora entre en conflit avec plusieurs paquets fedora. Il ne peut donc être utilisé tel quel.
=> vous noterez que alien rajoute un numéro à cyclope en le convertissant de debian (cyclope_1.5-5_all.deb) à fedora (cyclope-1.5-6.noarch.rpm)

Vérification si ce paquet aurait été toutefois installé : 

[root@Host-002 CYCLOPE]# rpm -e *.rpm
erreur : le paquet cyclope-1.5-6.noarch.rpm n'est pas installé

=> Le paquet cyclope ne peut être désintallé, puisqu'il n'a pas été installé.

[root@Host-002 CYCLOPE]# exit

Si tout est script dans unix, et si linux est un unix qui s'ignore, alors il reste la solution d'utiliser cyclope comme un simple script python, plutôt que comme un paquet en bonne et due forme malheureusement conflictuel.


5 / Procédure d’extraction du paquet cyclope (pour debian) :

L'idée est donc de suivre la procédure classique d'extraction d'un paquet deb

stevie  ~  CYCLOPE  ar xv *.deb
x - debian-binary
x - control.tar.gz
x – data.tar.xz

=> Il en résulte trois fichiers dénommés debian-binary, control.tar.gz et data.tar.xz, soit :


Dépaquetage un paquet *.deb

$ ar xv *.deb

Cela nous fournit 3 fichiers:
- debian-binary : fichier indiquant la version du paquet deb, soit 2.0, très différent de 1.5.5 affiché en dénomination du paquet ...
- data.tar.xz : fichier compressé contenant le code de cyclope,
- control.tar.gz : fichier compressé de contrôl.

A ce stade, il convient de procéder au désossage de chaque sous-fichier :

$ tar xvf control.tar.gz

 stevie  ~  CYCLOPE  tar xvf control.tar.gz
./
./control
./md5sums

=> le fichier md5sums permet de vérifier l'intégrité de l'archive.
=> Ici, pas de fichier de configuration, sauf s'il a disparu en cours de route …

En naviguant dans le système avec le gestionnaire de fichier « ranger », le contenu du fichier « control » apparait alors :

Package: cyclope
Version: 1.5-5
Architecture: all
Maintainer: arnault perret <arpinux@member.fsf.org>
Installed-Size: 99
Depends: python (>= 2.7), python-gtk2
Section: x11
Priority: optional
Homepage: https://handylinux.org/wiki/doku.php/fr/media#cyclope
Description: Une visionneuse d'images minimale.
 Cyclope vous permet de visionner, pivoter ou zoomer
 vos images simplement.
 Vous pouvez aussi lancer un diaporama ou envoyer vos
 images vers d'autres applications.
 .
 C'est la visionneuse rapide du bureau HandyLinux.
~                                                                                                                                                                        
~                                                                                                                                                                      
~                                                                                                                                                                        
"~/CYCLOPE/control" 16L, 524C

       

=> Notons les dépendances python (>= 2.7) et python-gtk2 !

Et oui, cyclope participe d'Handylinux ! Mais également de KissOs-7.0 !

Décompression du fichier « data.tar.xz »

$ tar xvf data.tar.xz

Résultat :

stevie  ~  CYCLOPE  tar xvf data.tar.xz
./
./usr/
./usr/share/
./usr/share/doc/
./usr/share/doc/cyclope/
./usr/share/doc/cyclope/changelog.Debian.gz
./usr/share/doc/cyclope/copyright
./usr/share/man/
./usr/share/man/man1/
./usr/share/man/man1/cyclope.1.gz
./usr/share/locale/
./usr/share/locale/en/
./usr/share/locale/en/LC_MESSAGES/
./usr/share/locale/en/LC_MESSAGES/cyclope.mo
./usr/share/locale/fr/
./usr/share/locale/fr/LC_MESSAGES/
./usr/share/locale/fr/LC_MESSAGES/cyclope.mo
./usr/share/applications/
./usr/share/applications/cyclope.desktop
./usr/share/pixmaps/
./usr/share/pixmaps/cyclope_icon.png
./usr/bin/
./usr/bin/cyclope

En gros : un dossier usr, qui contient lui-même deux sous-dossiers bin et share :

Le sous-dossier share comprend à son tour 5 sous-dossiers :
- pixmaps, icone du paquet
- locale, usage du paquet en langue française et anglaise
- man, manpage
- doc,
* changelog,
=> 
* copyright :

Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: cyclope

Files: *
Copyright: 2012 mickyz
           2015 Xavier Cartron <thuban@yeuxdelibad.net>
           2015 arpinux <arpinux@member.fsf.org>
License: GPL-3+
 This package 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 package 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.
 .
 You should have received a copy of the GNU General Public License
 along with this program. If not, see <http://www.gnu.org/licenses/>
 .
 On Debian systems, the complete text of the GNU General
 Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".

- applications,
fichier cyclope-desktop :

[Desktop Entry]
Name=Cyclope
Comment=Minimalist image viewer
Comment[fr]=Visionneuse d'images minimaliste
Exec=/usr/bin/cyclope
Icon=cyclope_icon
StartupNotify=true
Terminal=false
Type=Application
Categories=GTK;Graphics;Viewer;

Le sous-dossier bin comprend à son tour 1 seul fichier dénommé cyclope, constitué par un script python qui va nous intéresser désormais.


6 / Lancement du  script python dans l’environnement linux (cela marche également quelque soit l’unix utilisé, linux, BSD ...) :

Il nous reste donc à lancer le script python dans un environnement linux :

Après avoir installé les dépendances vues plus hauts python (>= 2.7) et python-gtk2, exécuter le script comme suit :

$ python cyclope # soit python nom_du_script_python_à_lancer

=> chez moi, cela donne cela :

stevie  ~  cd /home/stevie/CYCLOPE/CYCLOPE-TRAVAIL/usr/bin/
 stevie  …  CYCLOPE-TRAVAIL  usr  bin  dir
cyclope
 stevie  …  CYCLOPE-TRAVAIL  usr  bin  python cyclope
/usr/share/themes/Arc-REDemption/gtk-2.0/apps.rc:88: Incapable de localiser le fichier image dans le chemin des pixmaps : « assets/combo-entry-border.png »
/usr/share/themes/Arc-REDemption/gtk-2.0/apps.rc:92: Background image options specified without filename
/usr/share/themes/Arc-REDemption/gtk-2.0/apps.rc:98: Incapable de localiser le fichier image dans le chemin des pixmaps : « assets/combo-entry-border-focus.png »
/usr/share/themes/Arc-REDemption/gtk-2.0/apps.rc:102: Background image options specified without filename
/usr/share/themes/Arc-REDemption/gtk-2.0/apps.rc:109: Incapable de localiser le fichier image dans le chemin des pixmaps : « assets/combo-entry-border-rtl.png »
/usr/share/themes/Arc-REDemption/gtk-2.0/apps.rc:113: Background image options specified without filename
/usr/share/themes/Arc-REDemption/gtk-2.0/apps.rc:119: Incapable de localiser le fichier image dans le chemin des pixmaps : « assets/combo-entry-border-focus-rtl.png »
/usr/share/themes/Arc-REDemption/gtk-2.0/apps.rc:123: Background image options specified without filename
/usr/share/themes/Arc-REDemption/gtk-2.0/xfce-notify.rc:1: error: unexpected character '\', expected keyword - e.g. `style'

Une fenêtre de choix de fichiers images s'ouvre alors et permet de choisir les fichiers images à visionner. Il convient de naviguer sur le disque dur, car cyclope s'ouvrira sur le dossier de localisation du script !

Manuel raccourci de l'utilisation de cyclope :

Le menu clic-droit souris de cyclope propose plusieurs entrées (items) :

menu clic-droit souris de cyclope proposant plusieurs entrées

***

- ouvrir
- tourner côté droit,
- tourner côté gauche
- zoom,
- enclencher le défilement des photos,
- traiter l'image avec Gimp, logiciel reconnu par cyclope comme installé dans le système
- Traiter l'image avec Shotwell, logiciel reconnu par cyclope comme installé dans le système
- Aide :

cyclope
version : 1.5
author : Thuban
licence : GPLv3
homepage : https://handylinux.org
---
usage : # utilisation : 
Use arrows, b and n keys to change image # utiliser les flèches ou les touches b et n pour faire défiler les images 
Double click or press f key to enlarge the window # Double cliquer ou appuyer la lettre « f » pour augmenter la taille de l’image
Press o to open a new image # utiliser la touche o pour ouvrir une image
Press d or g to turn the image # utiliser les touches d ou g pour faire pivoter l'image 
Press z to toggle zoom mode # Appuyer la lettre « z » pour zoomer
Press = to restore the image # utiliser la touche z pour passer en mode zoom : sélectionner alors une zone avec votre curseur, le zoom sera lancé une fois le clic laché
Press p to start slideshow mode # utiliser la touche p pour lancer ou stopper le diaporama
Press m or right click to show the menu # utiliser la touche m ou le clic-droit pour afficher le menu
Press left click to show the menu # maintenir le clic-gauche pour déplacer la fenêtre
Press Escape or q to quit #  utiliser ESC ou q pour quitter

=> Chez moi, cela fonctionne parfaitement.
=> Cette aide s’avère légèrement modifier quant à la version originale. En effet, la ligne « Press left click to show the menu # maintenir le clic-gauche pour déplacer la fenêtre » a été rajouté, afin de paralléliser le fichier d’aide avec le fichier cyclope.1.gz
=> Dans le cas où vous ne sachiez pas configurer un paquet, ne modifiez rien, laissez tout comme cela …
=> La traduction française n’est de moi mais de Thuban à copié à partir du fichier cyclope.1.gz décompressable à l’adresse /home/$user/…/ usr/share/man/man1/ soit :


traduction française de l'aide

.TH CYCLOPE 1 "Sept, 08 2015"
.SH NAME
cyclope \- une visionneuse d'images par thuban
.SH DESCRIPTION
.B cyclope
est une visionneuse d'image en python ultra-légère
.br
.SH USAGE
utiliser les flèches ou les touches b et n pour faire défiler les images
.br
utiliser la touche o pour ouvrir une image
.br
utiliser la touche p pour lancer ou stopper le diaporama
.br
utiliser la touche m ou le clic-droit pour afficher le menu
.br
utiliser la touche f ou le double-clic pour dés/activer le plein-écran.
.br
utiliser les touches d ou g pour faire pivoter l'image
.br
utiliser la touche z pour passer en mode zoom : sélectionner alors une zone avec votre curseur, le zoom sera lancé une fois le clic laché.
.br
utiliser la touche = pour quitter le mode zoom
.br
maintenir le clic-gauche pour déplacer la fenêtre
.br
utiliser ESC ou q pour quitter.
.br
.TH cyclope

- fermer

Pour installer le script cyclope dans votre système, il existe deux solutions s’offre à l’utilisateur :
- soit créer son propre paquet cyclope *.rpm car celui-ci n’existe pas encore,
- soit installer le script et se contenter de le rendre opérationnel, sans plus.

A titre personnel, j’utilise la deuxième solution, car actuellement je ne sais pas construire un paquet rpm !


Il s’avère toutefois possible d’intégrer cyclope au menu clic-droit souris via le paquet via le menu.xml ou via le paquet obmenu.

Ce qui nous donne :


intégration de cyclope au menu clic-droit souris

Nous lisons bien « python /home/stevie/CYCLOPE/CYCLOPE-TRAVAIL/usr/bin/cyclope » ce qui correspond à la ligne de commande personnel de lancement de cyclope.

De plus, venant de Livarp, j’ai l’habitude de concevoir une distribution ou sa dérivée, comme une suite de script relié ensemble pour que cela fonctionne. « Dans unix, tout est script !».


7 / Manuel d’utilisation du script cyclope :

Exemple de clic-droit souris intégrant le script cyclope dans le menu openbox :

clic-droit souris intégrant le script cyclope dans le menu openbox

Nota bene : Ce serait la même chose avec le terminal, en intégrant le chemin de lancement du script

$ python /chemin_vers_votre_script_cyclope/cyclope

chemin_vers_votre_script_cyclope

Une fois lancée, le script cyclope ouvre une fenêtre de choix des fichiers à ouvrir :

ouverture d'une fenêtre de choix des fichiers à ouvrir

Choix d’ouvrir le dossier images, une fois le script lancée à partir du menu :

choix d’ouvrir le dossier images

Exemple de visionnage d’une photo sans l’option de plein écran :

visionnage d’une photo sans l’option de plein écran

Traitement photo avec le logiciel Gimp :

traitement photo avec le logiciel Gimp

traitement photo avec le logiciel Gimp

Photo visionné en plein écran :

photo visionné en plein écran

Fermeture du logiciel cyclope en clic-droit souris lors du visionnage d’une image en plein écran :

fermeture du logiciel cyclope en clic-droit souris


8 / Script cyclope à lancer avec python :
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# This application is released under the GNU General Public License 
# v3 (or, at your option, any later version). You can find the full 
# text of the license under http://www.gnu.org/licenses/gpl.txt
# By using, editing and/or distributing this software you agree to 
# the terms and conditions of this license.
#_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

# original application : eyez - !v1.0~mickyz(c)2012
# simplified to cyclope, a simple image viewer - thuban (yeuxdelibad.net) © 2015

import sys
import os
import gobject
import gtk
from gtk import gdk
import gettext

progname = "cyclope"
version = "1.5"
auteur = "Thuban" 
licence = "GPLv3" 
homepage= "https://handylinux.org"
cyclopeicon = "/usr/share/pixmaps/cyclope_icon.png"
minimal_w = 420
minimal_h = 180
screen_ratio = 0.85

EXT = ('.bmp','.ico','.gif','.jpg','.jpeg','.png','.svg','.svgz','.tif','.tiff')
editors = [{'app' :'gimp', 'name' :'gimp'},\
        {'app' :'lodraw', 'name' :'Libreoffice Draw'},
        {'app' :'gthumb', 'name' :'gthumb'},\
        {'app' :'gpaint', 'name' :'gpaint'},\
        {'app' :'shotwell', 'name' :'shotwell'}]

gettext.bindtextdomain(progname, '/usr/share/locale')
gettext.textdomain(progname)
_ = gettext.gettext

def listFiles(directory):
    files = [ f for f in sorted(os.listdir(directory)) if os.path.splitext(f.lower())[1] in EXT ]
    return(files)

def help():
    print("usage : {} /path/to/picture".format(sys.argv[0]))

def error_dlg(error):
    m = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,\
        buttons = gtk.BUTTONS_OK)
    m.set_markup(_("""Error while opening this file. Sorry \n {}""").format(error))
    ret = m.run()
    if ret == gtk.RESPONSE_DELETE_EVENT or ret == gtk.RESPONSE_OK:
        m.destroy()
    sys.exit(1)

def select_image():
    """return choosed filename or None"""
    chooser = gtk.FileChooserDialog(title=_("Open a picture"),action=gtk.FILE_CHOOSER_ACTION_OPEN,\
                buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
    chooser.set_current_folder(os.getcwd())
    filter = gtk.FileFilter()
    filter.set_name(_("Images"))
    mimes = ["image/png", "image/jpeg", "image/gif"]
    for m in mimes:
        filter.add_mime_type(m)
    patterns = [ "*.bmp", "*.ico", "*.gif", "*.jpg", "*.jpeg", "*.png",\
            "*.svg", "*.svgz", "*.tif", "*.tiff" ]
    for p in patterns :
        filter.add_pattern(p)
    chooser.add_filter(filter)

    response = chooser.run()
    if response == gtk.RESPONSE_OK:
        i = chooser.get_filename()
        chooser.destroy()
        return(i)
    else:
        print(_('Closed, no files selected'))
        chooser.destroy()
        return(None)

class Cyclope():
    def __init__(self, img):
        # variables
        self.imginfo = { 'path': str(), 'pixbuf': str(), 'is_static': True, 'width':0, 'height':0 }
        self.isfull = False # fullscreen or not
        self.oldtime = 0
        
        # to zoom or not to zoom
        self.zoommode = False 
        self.tozoom = False
        self.xz, self.yz, self.wz, self.hz = 0, 0, 0, 0

        self.diapomode = False

        self.image = gtk.Image() # L'image affichée

        # La fenêtre principale
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_position(gtk.WIN_POS_CENTER_ALWAYS)
        self.window.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#222222")) # Fond sombre

        # La taille de l'écran
        self.sw,self.sh = gdk.screen_width() *screen_ratio, gdk.screen_height() *screen_ratio

        # Les boutons de la fenêtre
        self.rightarrow = gtk.Button(stock=gtk.STOCK_GO_FORWARD)
        self.leftarrow = gtk.Button(stock=gtk.STOCK_GO_BACK)
        self.fullbtn = gtk.Button(stock=gtk.STOCK_FULLSCREEN)
        self.quitbtn = gtk.Button(stock=gtk.STOCK_QUIT)

        self.rightarrow.connect("button_release_event", lambda x,y: self.skipImage(+1))
        self.leftarrow.connect("button_release_event", lambda x,y: self.skipImage(-1))
        self.fullbtn.connect("button_release_event", lambda x,y: self.toggle_fullscreen())
        self.quitbtn.connect("button_release_event", lambda x,y: self.quit_app())

        self.buttons = gtk.HBox(True,0)
        self.buttons.pack_start(self.leftarrow,False, True,0)
        self.buttons.pack_start(self.rightarrow, False,True,0)
        self.buttons.pack_start(self.fullbtn, False,True,0)
        self.buttons.pack_start(self.quitbtn, False,True,0)

        for i in self.buttons: # disable keyboard focus
            if type(i) == gtk.Button: i.set_can_focus(False)

        # boite contenant l'image
        self.evbox = gtk.EventBox()
        self.evbox.add(self.image)
        self.evbox.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse('#222222')) # fond sombre
        # pour récupérer les informations nécessaires au zoom
        self.evbox.connect("button_release_event", self.apply_zoom)
        self.evbox.connect("motion_notify_event", self.motion_notify_event)
        
        # pour pas que la evbox prenne toute la place
        imgbox = gtk.HBox(False,0)
        imgbox.pack_start(self.evbox,True,False,0)
        
        # La boite qui contient tout
        self.container = gtk.VBox(False,0)
        self.container.pack_start(imgbox, True, False,0)
        self.container.pack_start(self.buttons, False, False, 0)

        # configuration de la fenêtre
        self.window.add(self.container)
        self.window.set_border_width(4) # pour avoir une bordure 
        self.window.set_decorated(False)
        if os.path.isfile(cyclopeicon):
            self.window.set_icon_from_file(cyclopeicon)
        
        self.window.add_events(gdk.BUTTON_PRESS_MASK | gdk.KEY_PRESS_MASK | gdk.SCROLL_MASK)
        self.window.connect('button_press_event', self.onButtonPress)
        self.window.connect('key_release_event', self.onKeyPress)
        self.window.connect('delete_event', self.quit_app)

        self.window.show_all()
        self.set_image(img)

        gtk.main()

        
    def quit_app(self):
        self.window.hide()
        gtk.main_quit()
        return(True)
    
    def diaporama(self):
        if not self.diapomode:
            self.diapomode = True
            gobject.timeout_add(2000, self.diapo_cycle)
        else:
            self.diapomode = False

    def diapo_cycle(self):
        if self.diapomode:
            self.skipImage(+1)
            gobject.timeout_add(3000, self.diapo_cycle)

    def back_to_nomal(self):
        self.set_image(self.imginfo['path'])
        self.updateimage()

    def toggle_zoom(self):
        if self.zoommode == True:
            self.zoommode = False
            self.evbox.window.set_cursor(None) 
            self.back_to_nomal()
        else:
            self.zoommode = True
            watch = gtk.gdk.Cursor(gtk.gdk.CROSS)
            self.evbox.window.set_cursor(watch)      
    
    def apply_zoom(self, widget, event):
        # on appuie sur entrée pour valider le zoom
        if self.zoommode and self.imginfo['is_static'] and self.tozoom : # pas besoin d'aller plus loin sinon
            # dimensions de la grande image
            oldw, oldh = self.imginfo['width'], self.imginfo['height']
            
            pix=self.image.get_pixbuf() # on récupère l'image actuelle
            zoomed_pix = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, self.wz, self.hz)
            pix.copy_area(self.xz, self.yz, self.wz, self.hz, zoomed_pix, 0, 0)  # on garde que la zone souhaitée

            # on met l'image aux dimensions de la fenêtre précédente
            ow,oh = self.sw, self.hz*self.sw/self.wz

            zoomed_pix = zoomed_pix.scale_simple(int(ow),int(oh), gdk.INTERP_BILINEAR)
            # mise à jour des informations de l'image
            self.imginfo['pixbuf'] = zoomed_pix
            self.imginfo['width'], self.imginfo['height'] = zoomed_pix.get_width(), zoomed_pix.get_height()
            
            self.image.set_from_pixbuf(zoomed_pix)
            
            self.updateimage()
            self.tozoom = False

            del pix
            del zoomed_pix            
                
    def motion_notify_event(self, widget, event):
        if self.zoommode and self.imginfo['is_static']: # pas besoin d'aller plus loin sinon
            # dimensions de la grande image
            oldw, oldh = self.imginfo['width'], self.imginfo['height']
            # récupération des données
            x, y = int(event.x), int(event.y)
            
            # attention au dépassements
            if x > oldw : x = oldw
            if x < 0 : x = 0
            if y > oldh : y = oldh
            if y < 0 : y = 0
            
            if x > self.xz_orig:
                self.wz = x - self.xz_orig
                self.xz = self.xz_orig
            else:
                self.wz = abs(x - self.xz_orig)
                self.xz = x

            if y > self.yz_orig:
                self.hz = y - self.yz_orig
                self.yz = self.yz_orig
            else:
                self.hz = abs(y - self.yz_orig)
                self.yz = y
            
            state = event.state
        
            if state & gtk.gdk.BUTTON1_MASK:
                cr = self.image.window.cairo_create()
                cr.set_source_pixbuf(self.image.get_pixbuf(),0,0) # pour effacer l'ancien rect
                cr.paint()
                cr.stroke()
                cr.set_source_rgba(0.25, 0.69, 1, 0.5)
                cr.rectangle(self.xz, self.yz, self.wz, self.hz)
                cr.fill()
                self.tozoom = True
    
    def set_image(self, img):
        """ Render the Pixbuf and show the image """
        self.get_infos(img)

        self.updateimage()

        # Quelques infos dans le titre de la fenêtre
        self.window.set_title('{} ! {}x{}'.format(\
                os.path.basename(img), self.imginfo['width'], self.imginfo['height']))

    def get_infos(self, img):
        """put image path, pixbuf and bool_static in self.imginfo"""
        if img.endswith('.gif'):
            pix = gdk.PixbufAnimation(img)
            if pix.is_static_image():
                self.imginfo = { 'path': '', 'pixbuf': '', 'is_static': True, 'width':0, 'height':0 }
                self.imginfo['path'] = img
                self.imginfo['pixbuf'] = pix.get_static_image().apply_embedded_orientation()
                self.imginfo['is_static'] = True
            else:
                self.imginfo['path'] = img
                self.imginfo['pixbuf'] = pix
                self.imginfo['is_static'] = False
        else:
            self.imginfo['path'] = img
            self.imginfo['pixbuf'] = gdk.pixbuf_new_from_file(img).apply_embedded_orientation()
            self.imginfo['is_static'] = True

        self.imginfo['width'] = self.imginfo['pixbuf'].get_width()
        self.imginfo['height'] = self.imginfo['pixbuf'].get_height()

    def updateimage(self):
        gap = self.buttons.get_allocation().height # taille des boutons à réserver        
        pix = self.imginfo['pixbuf']
        iw,ih = self.imginfo['width'], self.imginfo['height']
        
        # on cherche la taille de l'image la plus adaptée
        ow,oh = iw,ih
        if not self.isfull:
            if iw > self.sw: 
                ow,oh = self.sw, ih*self.sw/iw 
            if oh > self.sh: 
                ow,oh = iw*self.sh/ih, self.sh
        else:
            if iw > self.sw or ih > self.sh:
                ow,oh = self.sw/screen_ratio, ih*self.sw/screen_ratio/iw - gap
                if oh > self.sh: 
                    ow,oh = iw*self.sh/screen_ratio/ih, self.sh/screen_ratio - gap

        # on redimensionne l'image
        if self.imginfo['is_static']:
            pix = pix.scale_simple(int(ow),int(oh), gdk.INTERP_BILINEAR)
            self.image.set_from_pixbuf(pix)
        else:
            self.image.set_from_animation(pix)
        del pix

        # on change la taille de la fenêtre
        if self.isfull:
            self.window.resize(gdk.screen_width(),gdk.screen_height())
        else:
            if ow < minimal_w:
                self.window.resize(minimal_w, int(oh+gap))
            else:
                self.window.resize(int(ow),int(oh+gap))
                
    def toggle_fullscreen(self):
        if self.isfull: # on passe en taille normale
            self.window.unfullscreen()
            self.buttons.show()
            self.isfull = False
            self.updateimage()
            
            self.fullbtn.set_label(_("Fullscreen"))
            image = gtk.Image()
            image.set_from_stock(gtk.STOCK_FULLSCREEN, gtk.ICON_SIZE_MENU)
            self.fullbtn.set_image(image)

        else:# On passe en plein écran
            self.isfull = True
            self.window.fullscreen()
            self.buttons.hide()
            self.updateimage()

            self.fullbtn.set_label(_("Restore screen"))
            image = gtk.Image()
            image.set_from_stock(gtk.STOCK_UNDO, gtk.ICON_SIZE_MENU)
            self.fullbtn.set_image(image)

    def open_image(self):
        i = select_image()
        if i :
            self.set_image(i)

    def onButtonPress(self, widget, event):
        if self.zoommode and self.imginfo['is_static']: # pas besoin d'aller plus loin sinon
            if event.button == 1:
                self.xz_orig, self.yz_orig = int(event.x), int(event.y)
        else:
            if event.button == 1 :
                if event.type == gdk._2BUTTON_PRESS: # double clic gauche
                    self.toggle_fullscreen()
                elif not self.isfull and (event.get_time() - self.oldtime > 500 ):
                    self.window.begin_move_drag(event.button, int(event.x_root), int(event.y_root), event.time)
                    self.oldtime = event.get_time()
        if event.button == 3 or event.button == 2: # n'importe quel autre bouton
            self.onMenuPopup(event.get_time())
            

    def onKeyPress(self, win, event):
        key = gdk.keyval_name(event.keyval)
        if key == 'Escape' or key == 'q':
            if not self.isfull:
                self.quit_app()
            else:
                if self.diapomode :
                    self.diapomode = False
                self.toggle_fullscreen()
        elif key == 'p':
            self.diaporama()
        elif key == 'z':
            self.toggle_zoom()
        elif key == 'equal':
            self.back_to_nomal()
        elif key == 'f':
            self.toggle_fullscreen()
        elif key == 'Right' or key == 'n':
            self.skipImage(+1)
        elif key == 'Left' or key == 'b':
            self.skipImage(-1)
        elif key == 'o': 
            self.open_image()
        elif key == 'm': 
            self.onMenuPopup(event.get_time())
        elif key == 'd': 
            self.rotate(90)
        elif key == 'g': 
            self.rotate(-90)
        elif key == 'h': 
            self.window.set_title("Handylinux rocks!")
        elif key == 'a': 
            self.window.set_title("arpinux needs beers!")

    def onMenuPopup(self, time=0):
        self.app_menu = self.set_menu()
        self.app_menu.show_all()
        self.app_menu.popup(None, None, None, 3, time, 0)

    def set_menu(self):
        menu = gtk.Menu()

        op = gtk.ImageMenuItem(gtk.STOCK_OPEN)
        op.connect('activate', lambda w: self.open_image())
        menu.append(op)
        menu.append(gtk.SeparatorMenuItem())

        rotater = gtk.ImageMenuItem(gtk.STOCK_CONVERT)
        rotater.set_label(_("Rotate right"))
        rotater.connect('activate', lambda w: self.rotate(90))
        menu.append(rotater)

        rotatel = gtk.ImageMenuItem(gtk.STOCK_CONVERT)
        rotatel.set_label(_("Rotate left"))
        rotatel.connect('activate', lambda w: self.rotate(-90))
        menu.append(rotatel)
        
        zoom = gtk.ImageMenuItem(gtk.STOCK_ZOOM_IN)
        if self.zoommode :
            zoom.set_label(_("Disable zoom"))
            zoom.connect('activate', lambda w: self.toggle_zoom())
        else:
            zoom.set_label(_("Zoom"))
            zoom.connect('activate', lambda w: self.toggle_zoom())
        menu.append(zoom)
        
        if self.diapomode:
            diapo = gtk.ImageMenuItem(gtk.STOCK_MEDIA_STOP)
            diapo.set_label(_("Disable slideshow"))
        else:
            diapo = gtk.ImageMenuItem(gtk.STOCK_MEDIA_PLAY)
            diapo.set_label(_("Enable slideshow"))
        diapo.connect('activate', lambda w: self.diaporama())
        menu.append(diapo)
        
        for editor in editors:
            if os.path.exists('/usr/bin/{}'.format(editor['app'])):
                edit = gtk.ImageMenuItem(gtk.STOCK_EDIT)
                edit.set_label(_("Edit with {}").format(editor['name']))
                edit.connect('activate', self.openWith, editor['app'])
                menu.append(edit)

        menu.append(gtk.SeparatorMenuItem())
        apropos = gtk.ImageMenuItem(gtk.STOCK_HELP)
        apropos.connect('activate', self.about)
        menu.append(apropos)
        close = gtk.ImageMenuItem(gtk.STOCK_CLOSE)
        close.connect('activate', lambda w: self.quit_app())
        menu.append(close)

        return menu

    def skipImage(self, position):
        path = self.imginfo['path']
        d, f = os.path.dirname(path), os.path.basename(path)
        files = listFiles(d)
        for n,i in enumerate(files):
            if f == i:
                try: f = files[n+position]
                except: f = files[0]
                self.set_image(os.path.join(d, f))
                break

    def rotate(self,angle):
        pix = self.imginfo['pixbuf']
        if angle == 90:
            rotated = pix.rotate_simple(gtk.gdk.PIXBUF_ROTATE_CLOCKWISE)
        elif angle == -90:
            rotated = pix.rotate_simple(gtk.gdk.PIXBUF_ROTATE_COUNTERCLOCKWISE)

        self.imginfo['pixbuf'] = rotated
        self.imginfo['width'], self.imginfo['height'] = rotated.get_width(), rotated.get_height()

        if self.imginfo['is_static']:
            self.image.set_from_pixbuf(rotated)
        else:
            self.image.set_from_animation(rotated)
        self.updateimage()
        del pix
        del rotated

    def openWith(self, widget, data=None):
        import subprocess
        try:
            subprocess.Popen([data, self.imginfo['path']])
        except:
            print("Error at opening {} with {}".format(self.imginfo['path'], data))

    def about(self, wid=None, data=None):
        # fenêtre à propos.
        m = gtk.MessageDialog(type=gtk.MESSAGE_INFO,\
            buttons = gtk.BUTTONS_OK)
        m.set_markup(_("""<b>{}</b>
version : {}
author : {}
licence : {}
homepage : {}
---
usage :
Use arrows, b and n keys to change image
Double click or press f key to enlarge the window
Press o to open a new image
Press d or g to turn the image
Press z to toggle zoom mode
Press = to restore the image
Press p to start slideshow mode
Press m or right click to show the menu
Press Escape or q to quit""").format(progname, version, auteur, licence, homepage))
        ret = m.run()
        if ret == gtk.RESPONSE_DELETE_EVENT or ret == gtk.RESPONSE_OK:
            m.destroy()

def main():
    if len(sys.argv) == 1 :  # si aucune image en argument
        img = select_image()
        if not img :
            sys.exit()       # on quitte s'il n'y a pas d'image choisie
    elif len(sys.argv) == 2:
        img = os.path.abspath(sys.argv[1])

    if os.path.isdir(img):   # si c'est un répertoire
        imglist = sorted(os.listdir(img))
        if len(imglist) > 0: # on ouvre les images du répertoire
            img = os.path.join(img,imglist[0])

    try: 
        window = Cyclope(img) # ouverture d'une image
    except Exception as e:
        help()
        error_dlg(e)

    return 0

if __name__ == '__main__':
    main()

# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
Conclusion :

Voilà, cyclope fonctionne bien sous Fedora (n, n+1, ...).

Le principe devrait être le même quelque soit le système unix employé, y compris NetBSD et OpenBSD.

Ne sachant pas faire de paquet rpm, je me contente de cette démarche empirique, mais qui fonctionne, puisque fondée sur le principe basique que tout est script sous unix.

En vous souhaitant un bon usage du script cyclope.

Bien à vous.

Mr. S.


Sources :

- Sources.list d’Handylinux : http://repo.handylinux.org/handylinux.list
- article « fabrication de package rpm » : http://eric.gerbier.free.fr/fabrication-rpm.html,
- article « Créer un package RPM » : http://wiki.gantzer.eu/index.php/Cr%C3% … ackage_RPM,
- forum openclassroom : https://openclassrooms.com/forum/sujet/ … -sur-linux,
- fonds d’écran « Viperr 9 » : https://viperr.org/site/img/DesktopV9.png.

Hors ligne

#2 09-10-2017 19:46:17

penthium2
Modérateur

Re : Portage du paquet debian stretch "cyclope" vers fedora 26

j'avous pas eu le courage de tout lire par manque de balise "code"

quand c'est une sortie de terminal merci de mettre les bonne balises.


vi est mon ami pour la vie
Viperr
Ph'nglui nglw-nafh Cthulhu R'lyeh wgah-nagl fhtagn

Hors ligne

#3 10-10-2017 06:49:18

IceF0x
#! Gourou Linux

Re : Portage du paquet debian stretch "cyclope" vers fedora 26

Viperr a écrit :

j'avous pas eu le courage de tout lire par manque de balise "code"

quand c'est une sortie de terminal merci de mettre les bonne balises.

Je confirme, c'est illisible sans mise en forme.


Utiliser des logiciels propriétaires, c'est comme les plats préparés, on est incapable de dire les conservateurs qu'ils contiennent, on dira toujours que c'est bon, mais ça ne remplacera jamais le repas fait maison par sa maman.
]:D #! Crunchbang & Archlinux GNU/Linux User ]:D

Hors ligne

#4 11-10-2017 09:47:26

Mr. S.
Membre

Re : Portage du paquet debian stretch "cyclope" vers fedora 26

Bonjour,

Pourriez-vous m'expliquer comment rédiger avec ce que vous appelez des balises codes ?

En effet, cela me permettrait de reprendre ce tutoriel de manière à le rendre plus lisible et compréhensible.

Par ailleurs, comment intégrer les photos dans le texte du post ?

Je possède quelques tutoriels en réserves que j'hésite à vous communiquer pour cette raison.

Merci d'avance.

Mr. S.

Hors ligne

#5 11-10-2017 09:58:21

IceF0x
#! Gourou Linux

Re : Portage du paquet debian stretch "cyclope" vers fedora 26

Normalement tu as les boutons au dessus de la zone texte pour mettre en forme le texte.

Sinon une balise code c'est ceci

[code]ton code ici[/code]

Pour les images c'est

[img]lien vers l'image ici[/img]

exemple:

[img]https://upload.wikimedia.org/wikipedia/commons/thumb/5/53/GNU_and_Tux.svg/620px-GNU_and_Tux.svg.png[/img]

Ce qui donne:


620px-GNU_and_Tux.svg.png


Utiliser des logiciels propriétaires, c'est comme les plats préparés, on est incapable de dire les conservateurs qu'ils contiennent, on dira toujours que c'est bon, mais ça ne remplacera jamais le repas fait maison par sa maman.
]:D #! Crunchbang & Archlinux GNU/Linux User ]:D

Hors ligne

#6 15-10-2017 16:42:04

Mr. S.
Membre

Re : Portage du paquet debian stretch "cyclope" vers fedora 26

Bonjour tout le monde,

Je viens de refaire ma copie en tenant compte de vos remarques tout à fait justifiées.

J'espère que je ne me suis pas trop trompé dans l'usage des balises codes.

Merci donc à Viperr et à IceFox.

Bonne lecture à tous donc et que ce tutoriel vous soit des plus utiles !

Portage du paquet debian stretch "cyclope" vers fedora 26
Sommaire :

Introduction
1 / Echec de dpkg-repack sous KissOs-7.0
2 / téléchargement du paquet cyclope (pour debian) depuis le dépôt handylinux
3 /  Création d'un dossier de travail
4 / Echec de l’utilisation du paquet alien pour transformer le paquet cyclope (pour debian) en paquet pour fedora
5 / Procédure d’extraction du paquet cyclope (pour debian)
6 / Lancement du  script python dans l’environnement linux (cela marche également quelque soit l’unix utilisé, linux, BSD ...)
7 / Manuel d’utilisation du script cyclope
8 / Script cyclope à lancer avec python
Conclusion
Sources



Introduction

Il s’agit ici non seulement de vous présenter le paquet cyclope, visionneuse photo ultra-légère, conçu par Thuban) mais encore de vous permettre de l’utiliser sur n’importe quelle système unix [linux (Arch, debian, fedora, gentoo, void, …) ou BSD (Net, Open …) et que sais-je encore !

Ma plate-forme de travail a autant été :

- un acer aspire 9410 sous KissOs-0.7 mono-boot :

1508069111.png

Exemple de mon bureau sur KissOs-0.7 : un simple rajout de cairo-dock qui rend bien service !

- un lenovo G50-45 UEFI SECURE BOOT sous Viperr Fedora remix 9 fc 26, dopée aux kickstarts fedora labs (https://github.com/SamsungARTIK/fedora-spin-kickstarts). D’abord dual boot avec Windows 8/10, puis mono boot (mais toujours UEFI SECURE BOOT!) car j’en avais marre des mises à jour longues de Windows dont je ne me sers plus à la maison depuis 2010 environ.

Ce qui nous donne ceci :

?img=1508069111.png


1 / Echec de dpkg-repack sous KissOs-0.7
stevie@kiss0s ~
$ dpkg-repack cyclope
dpkg-repack: Fatal Error: This program should be run as root (or you could use fakeroot -u). Aborting.
stevie@kiss0s ~
$ fakeroot -u
bash: fakeroot : commande introuvable
stevie@kiss0s ~
$ sudo apt-get -y install fakeroot
[sudo] Mot de passe de stevie : 
Lecture des listes de paquets... Fait
Construction de l'arbre des dépendances 
Lecture des informations d'état... Fait
The following additional packages will be installed:
libfakeroot
Les NOUVEAUX paquets suivants seront installés :
fakeroot libfakeroot
0 mis à jour, 2 nouvellement installés, 0 à enlever et 1 non mis à jour.
Il est nécessaire de prendre 134 ko dans les archives.
Après cette opération, 369 ko d'espace disque supplémentaires seront utilisés.
Réception de:1 http://deb.debian.org/debian stretch/main i386 libfakeroot i386 1.21-3.1 [46,8 kB]
Réception de:2 http://deb.debian.org/debian stretch/main i386 fakeroot i386 1.21-3.1 [86,8 kB]
134 ko réceptionnés en 0s (231 ko/s)
Sélection du paquet libfakeroot:i386 précédemment désélectionné.
(Lecture de la base de données... 80611 fichiers et répertoires déjà installés.)
Préparation du dépaquetage de .../libfakeroot_1.21-3.1_i386.deb ...
Dépaquetage de libfakeroot:i386 (1.21-3.1) ...
Sélection du paquet fakeroot précédemment désélectionné.
Préparation du dépaquetage de .../fakeroot_1.21-3.1_i386.deb ...
Dépaquetage de fakeroot (1.21-3.1) ...
Traitement des actions différées (« triggers ») pour libc-bin (2.24-11+deb9u1) ...
Paramétrage de libfakeroot:i386 (1.21-3.1) ...
Traitement des actions différées (« triggers ») pour man-db (2.7.6.1-2) ...
Paramétrage de fakeroot (1.21-3.1) ...
update-alternatives: utilisation de « /usr/bin/fakeroot-sysv » pour fournir « /usr/bin/fakeroot » (fakeroot) en mode automatique
Traitement des actions différées (« triggers ») pour libc-bin (2.24-11+deb9u1) ...
stevie@kiss0s ~
$ fakeroot -u
root@kiss0s ~
$ sudo dpkg-repack cyclope
ERROR: ld.so: object 'libfakeroot-sysv.so' from LD_PRELOAD cannot be preloaded (cannot open shared object file): ignored.
dpkg-query: le paquet « cyclope » n'est pas installé et aucune information n'est disponible
Utilisez dpkg --info (= dpkg-deb --info) pour examiner les fichiers
archives, et dpkg --contents (= dpkg-deb --contents) pour afficher leur
contenu.
Use of uninitialized value in pattern match (m//) at /usr/bin/dpkg-repack line 138.
Use of uninitialized value in concatenation (.) or string at /usr/bin/dpkg-repack line 139.
dpkg-repack: Fatal Error: Package cyclope not fully installed: 
root@kiss0s ~
$ exit
exit
stevie@kiss0s ~
$ fakeroot -u
root@kiss0s ~
$ dpkg-repack cyclope
dpkg-query: le paquet « cyclope » n'est pas installé et aucune information n'est disponible
Utilisez dpkg --info (= dpkg-deb --info) pour examiner les fichiers
archives, et dpkg --contents (= dpkg-deb --contents) pour afficher leur
contenu.
Use of uninitialized value in pattern match (m//) at /usr/bin/dpkg-repack line 138.
Use of uninitialized value in concatenation (.) or string at /usr/bin/dpkg-repack line 139.
dpkg-repack: Fatal Error: Package cyclope not fully installed: 
root@kiss0s ~
$

Ce qui donne ceci en copie écran :

?img=1508069960.png


2 / Téléchargement du paquet cyclope (pour debian) depuis le dépôt handylinux :

En partant du sources.list d'Handylinux (http://repo.handylinux.org/handylinux.list) :

 ## HandyLinux v1 ##
deb http://handylinux.org/repo/debian stable main
#deb-src http://handylinux.org/repo/debian stable main

## HandyLinux v1 Compiz ##
deb http://handylinux.org/repo/debian compiz main
#deb-src http://handylinux.org/repo/debian compiz main

## HandyLinux v2 Jessie ##
deb http://handylinux.org/repo/debian jessie main
#deb-src http://handylinux.org/repo/debian jessie main

## HandyLinux Testing ##
deb http://handylinux.org/repo/debian testing main
#deb-src http://handylinux.org/repo/debian testing main

=> Extraire la "bonne adresse", soit "http://handylinux.org/repo/debian/".

Nous obtenons ceci :

Index of /repo/debian

Icon  Name                    Last modified      Size  Description[DIR] Parent Directory                             -   
[DIR] conf/                   06-Jun-2017 18:58    -   
[DIR] db/                     06-Jun-2017 18:58    -   
[DIR] dists/                  06-Jun-2017 18:58    -   
[DIR] lists/                  06-Jun-2017 18:58    -   
[DIR] pool/                   06-Jun-2017 18:58

=> Se rendre dans le dossier "pool" :

=> Naviguer dans le dépôt jusqu'à l'url "http://handylinux.org/repo/debian/pool/main/c/"

Index of /repo/debian/pool/main/c

Icon  Name                    Last modified      Size  Description[DIR] Parent Directory                             -   
[DIR] ccsm/                   06-Jun-2017 18:59    -   
[DIR] compiz-deskmenu/        06-Jun-2017 18:59    -   
[DIR] compiz-fusion-plugin..> 06-Jun-2017 19:02    -   
[DIR] compiz-fusion-plugin..> 06-Jun-2017 19:00    -   
[DIR] compiz/                 06-Jun-2017 19:01    -   
[DIR] compizconfig-python/    06-Jun-2017 18:59    -   
[DIR] copcoll/                06-Jun-2017 18:59    -   
[DIR] cyclope/                06-Jun-2017 19:01 

=> Entrer dans cyclope : url "http://handylinux.org/repo/debian/pool/main/c/cyclope"

Index of /repo/debian/pool/main/c/cyclope

Icon  Name                    Last modified      Size  Description[DIR] Parent Directory                             -   
[   ] cyclope_1.5-5.debian..> 06-Jun-2017 19:01  5.1K  
[TXT] cyclope_1.5-5.dsc       06-Jun-2017 19:01  841   
[   ] cyclope_1.5-5_all.deb   06-Jun-2017 19:01   20K  
[   ] cyclope_1.5.orig.tar.xz 06-Jun-2017 19:01  201K
3 /  Création d'un dossier de travail :
$ mkdir /home/$user/CYCLOPE

=> Télécharger le fichier "cyclope_1.5-5_all.deb" par un clic droit souris

stevie ~ CYCLOPE dir
cyclope_1.5-5_all.deb
 stevie ~ CYCLOPE ls
cyclope_1.5-5_all.deb

=> les commandes ls et dir fonctionnent sous debian et sous fedora pour déterminer le contenu d'un dossier.


4 / Echec de l’utilisation du paquet alien pour transformer le paquet cyclope (pour debian) en paquet pour fedora :

stevie  ~  CYCLOPE  su
Mot de passe : 
[root@Host-002 CYCLOPE]# alien -r *.deb
cyclope-1.5-6.noarch.rpm generated
[root@Host-002 CYCLOPE]# rpm -ivh *.rpm
Préparation...                       ################################# [100%]
	le fichier /usr/bin de l'installation de cyclope-1.5-6.noarch entre en conflit avec le fichier du paquet viperr-obkey-8.0-viperr.fc24.noarch
	le fichier /usr/bin de l'installation de cyclope-1.5-6.noarch entre en conflit avec le fichier du paquet viperrconf-9.7-viperr.fc24.noarch
	le fichier /usr/bin de l'installation de cyclope-1.5-6.noarch entre en conflit avec le fichier du paquet viperr-conposite-8.0-viperr.fc24.noarch
	le fichier / de l'installation de cyclope-1.5-6.noarch entre en conflit avec le fichier du paquet filesystem-3.2-40.fc26.x86_64
	le fichier /usr/bin de l'installation de cyclope-1.5-6.noarch entre en conflit avec le fichier du paquet filesystem-3.2-40.fc26.x86_64

=> Le paquet cyclope alienisé en version fedora entre en conflit avec plusieurs paquets fedora. Il ne peut donc être utilisé tel quel.
=> vous noterez que alien rajoute un numéro à cyclope en le convertissant de debian (cyclope_1.5-5_all.deb) à fedora (cyclope-1.5-6.noarch.rpm)

Vérification si ce paquet aurait été toutefois installé : 

[root@Host-002 CYCLOPE]# rpm -e *.rpm
erreur : le paquet cyclope-1.5-6.noarch.rpm n'est pas installé

=> Le paquet cyclope ne peut être désintallé, puisqu'il n'a pas été installé.

[root@Host-002 CYCLOPE]# exit

Si tout est script dans unix, et si linux est un unix qui s'ignore, alors il reste la solution d'utiliser cyclope comme un simple script python, plutôt que comme un paquet en bonne et due forme malheureusement conflictuel.


5 / Procédure d’extraction du paquet cyclope (pour debian) :

L'idée est donc de suivre la procédure classique d'extraction d'un paquet deb

stevie  ~  CYCLOPE  ar xv *.deb
x - debian-binary
x - control.tar.gz
x – data.tar.xz

Soit en copie-écran :


?img=1508070136.png

=> Il en résulte trois fichiers dénommés debian-binary, control.tar.gz et data.tar.xz, soit :


Dépaquetage un paquet *.deb

$ ar xv *.deb

Cela nous fournit 3 fichiers:
- debian-binary : fichier indiquant la version du paquet deb, soit 2.0, très différent de 1.5.5 affiché en dénomination du paquet ...
- data.tar.xz : fichier compressé contenant le code de cyclope,
- control.tar.gz : fichier compressé de contrôl.

A ce stade, il convient de procéder au désossage de chaque sous-fichier :

$ tar xvf control.tar.gz

 stevie  ~  CYCLOPE  tar xvf control.tar.gz
./
./control
./md5sums

=> le fichier md5sums permet de vérifier l'intégrité de l'archive.


?img=1508070286.png

=> Ici, pas de fichier de configuration, sauf s'il a disparu en cours de route …

En naviguant dans le système avec le gestionnaire de fichier « ranger », le contenu du fichier « control » apparait alors :

Package: cyclope
Version: 1.5-5
Architecture: all
Maintainer: arnault perret <arpinux@member.fsf.org>
Installed-Size: 99
Depends: python (>= 2.7), python-gtk2
Section: x11
Priority: optional
Homepage: https://handylinux.org/wiki/doku.php/fr/media#cyclope
Description: Une visionneuse d'images minimale.
 Cyclope vous permet de visionner, pivoter ou zoomer
 vos images simplement.
 Vous pouvez aussi lancer un diaporama ou envoyer vos
 images vers d'autres applications.
 .
 C'est la visionneuse rapide du bureau HandyLinux.
~                                                                                                                                                                        
~                                                                                                                                                                      
~                                                                                                                                                                        
"~/CYCLOPE/control" 16L, 524C

       

=> Notons les dépendances python (>= 2.7) et python-gtk2 !

?img=1508070412.png

Et oui, cyclope participe d'Handylinux ! Mais également de KissOs-7.0 !

Décompression du fichier « data.tar.xz »

$ tar xvf data.tar.xz

Résultat :

stevie  ~  CYCLOPE  tar xvf data.tar.xz
./
./usr/
./usr/share/
./usr/share/doc/
./usr/share/doc/cyclope/
./usr/share/doc/cyclope/changelog.Debian.gz
./usr/share/doc/cyclope/copyright
./usr/share/man/
./usr/share/man/man1/
./usr/share/man/man1/cyclope.1.gz
./usr/share/locale/
./usr/share/locale/en/
./usr/share/locale/en/LC_MESSAGES/
./usr/share/locale/en/LC_MESSAGES/cyclope.mo
./usr/share/locale/fr/
./usr/share/locale/fr/LC_MESSAGES/
./usr/share/locale/fr/LC_MESSAGES/cyclope.mo
./usr/share/applications/
./usr/share/applications/cyclope.desktop
./usr/share/pixmaps/
./usr/share/pixmaps/cyclope_icon.png
./usr/bin/
./usr/bin/cyclope

En gros : un dossier usr, qui contient lui-même deux sous-dossiers bin et share :

Le sous-dossier share comprend à son tour 5 sous-dossiers :
- pixmaps, icone du paquet
- locale, usage du paquet en langue française et anglaise
- man, manpage
- doc,
* changelog,
=> 
* copyright :

Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: cyclope

Files: *
Copyright: 2012 mickyz
           2015 Xavier Cartron <thuban@yeuxdelibad.net>
           2015 arpinux <arpinux@member.fsf.org>
License: GPL-3+
 This package 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 package 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.
 .
 You should have received a copy of the GNU General Public License
 along with this program. If not, see <http://www.gnu.org/licenses/>
 .
 On Debian systems, the complete text of the GNU General
 Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".

?img=1508070523.png

- applications,
fichier cyclope-desktop :

[Desktop Entry]
Name=Cyclope
Comment=Minimalist image viewer
Comment[fr]=Visionneuse d'images minimaliste
Exec=/usr/bin/cyclope
Icon=cyclope_icon
StartupNotify=true
Terminal=false
Type=Application
Categories=GTK;Graphics;Viewer;

?img=1508070898.png

Le sous-dossier bin comprend à son tour 1 seul fichier dénommé cyclope, constitué par un script python qui va nous intéresser désormais.

?img=1508070966.png



6 / Lancement du  script python dans l’environnement linux (cela marche également quelque soit l’unix utilisé, linux, BSD ...) :


Il nous reste donc à lancer le script python dans un environnement linux :

Après avoir installé les dépendances vues plus hauts python (>= 2.7) et python-gtk2, exécuter le script comme suit :

$ python cyclope # soit python nom_du_script_python_à_lancer

=> chez moi, cela donne cela :

stevie  ~  cd /home/stevie/CYCLOPE/CYCLOPE-TRAVAIL/usr/bin/
 stevie  …  CYCLOPE-TRAVAIL  usr  bin  dir
cyclope
 stevie  …  CYCLOPE-TRAVAIL  usr  bin  python cyclope
/usr/share/themes/Arc-REDemption/gtk-2.0/apps.rc:88: Incapable de localiser le fichier image dans le chemin des pixmaps : « assets/combo-entry-border.png »
/usr/share/themes/Arc-REDemption/gtk-2.0/apps.rc:92: Background image options specified without filename
/usr/share/themes/Arc-REDemption/gtk-2.0/apps.rc:98: Incapable de localiser le fichier image dans le chemin des pixmaps : « assets/combo-entry-border-focus.png »
/usr/share/themes/Arc-REDemption/gtk-2.0/apps.rc:102: Background image options specified without filename
/usr/share/themes/Arc-REDemption/gtk-2.0/apps.rc:109: Incapable de localiser le fichier image dans le chemin des pixmaps : « assets/combo-entry-border-rtl.png »
/usr/share/themes/Arc-REDemption/gtk-2.0/apps.rc:113: Background image options specified without filename
/usr/share/themes/Arc-REDemption/gtk-2.0/apps.rc:119: Incapable de localiser le fichier image dans le chemin des pixmaps : « assets/combo-entry-border-focus-rtl.png »
/usr/share/themes/Arc-REDemption/gtk-2.0/apps.rc:123: Background image options specified without filename
/usr/share/themes/Arc-REDemption/gtk-2.0/xfce-notify.rc:1: error: unexpected character '\', expected keyword - e.g. `style'

Soit en copie-écran :

https://pix.tdct.org/?img=1508071141.png

Une fenêtre de choix de fichiers images s'ouvre alors et permet de choisir les fichiers images à visionner. Il convient de naviguer sur le disque dur, car cyclope s'ouvrira sur le dossier de localisation du script !

Manuel raccourci de l'utilisation de cyclope :

Le menu clic-droit souris de cyclope propose plusieurs entrées (items) :

?img=1508071249.png


***

- ouvrir
- tourner côté droit,
- tourner côté gauche
- zoom,
- enclencher le défilement des photos,
- traiter l'image avec Gimp, logiciel reconnu par cyclope comme installé dans le système
- Traiter l'image avec Shotwell, logiciel reconnu par cyclope comme installé dans le système
- Aide :

cyclope
version : 1.5
author : Thuban
licence : GPLv3
homepage : https://handylinux.org
---
usage : # utilisation : 
Use arrows, b and n keys to change image # utiliser les flèches ou les touches b et n pour faire défiler les images 
Double click or press f key to enlarge the window # Double cliquer ou appuyer la lettre « f » pour augmenter la taille de l’image
Press o to open a new image # utiliser la touche o pour ouvrir une image
Press d or g to turn the image # utiliser les touches d ou g pour faire pivoter l'image 
Press z to toggle zoom mode # Appuyer la lettre « z » pour zoomer
Press = to restore the image # utiliser la touche z pour passer en mode zoom : sélectionner alors une zone avec votre curseur, le zoom sera lancé une fois le clic laché
Press p to start slideshow mode # utiliser la touche p pour lancer ou stopper le diaporama
Press m or right click to show the menu # utiliser la touche m ou le clic-droit pour afficher le menu
Press left click to show the menu # maintenir le clic-gauche pour déplacer la fenêtre
Press Escape or q to quit #  utiliser ESC ou q pour quitter

=> Chez moi, cela fonctionne parfaitement.
=> Cette aide s’avère légèrement modifier quant à la version originale. En effet, la ligne « Press left click to show the menu # maintenir le clic-gauche pour déplacer la fenêtre » a été rajouté, afin de paralléliser le fichier d’aide avec le fichier cyclope.1.gz
=> Dans le cas où vous ne sachiez pas configurer un paquet, ne modifiez rien, laissez tout comme cela …
=> La traduction française n’est de moi mais de Thuban à copié à partir du fichier cyclope.1.gz décompressable à l’adresse /home/$user/…/ usr/share/man/man1/ soit :


?img=1508069679.png

.TH CYCLOPE 1 "Sept, 08 2015"
.SH NAME
cyclope \- une visionneuse d'images par thuban
.SH DESCRIPTION
.B cyclope
est une visionneuse d'image en python ultra-légère
.br
.SH USAGE
utiliser les flèches ou les touches b et n pour faire défiler les images
.br
utiliser la touche o pour ouvrir une image
.br
utiliser la touche p pour lancer ou stopper le diaporama
.br
utiliser la touche m ou le clic-droit pour afficher le menu
.br
utiliser la touche f ou le double-clic pour dés/activer le plein-écran.
.br
utiliser les touches d ou g pour faire pivoter l'image
.br
utiliser la touche z pour passer en mode zoom : sélectionner alors une zone avec votre curseur, le zoom sera lancé une fois le clic laché.
.br
utiliser la touche = pour quitter le mode zoom
.br
maintenir le clic-gauche pour déplacer la fenêtre
.br
utiliser ESC ou q pour quitter.
.br
.TH cyclope

- fermer

Pour installer le script cyclope dans votre système, il existe deux solutions s’offre à l’utilisateur :
- soit créer son propre paquet cyclope *.rpm car celui-ci n’existe pas encore,
- soit installer le script et se contenter de le rendre opérationnel, sans plus.

A titre personnel, j’utilise la deuxième solution, car actuellement je ne sais pas construire un paquet rpm !

Il s’avère toutefois possible d’intégrer cyclope au menu clic-droit souris via le paquet via le menu.xml ou via le paquet obmenu.

Ce qui nous donne :

1508071367.png


Nous lisons bien « python /home/stevie/CYCLOPE/CYCLOPE-TRAVAIL/usr/bin/cyclope » ce qui correspond à la ligne de commande personnel de lancement de cyclope.

De plus, venant de Livarp, j’ai l’habitude de concevoir une distribution ou sa dérivée, comme une suite de script relié ensemble pour que cela fonctionne. « Dans unix, tout est script !».


7 / Manuel d’utilisation du script cyclope :

Exemple de clic-droit souris intégrant le script cyclope dans le menu openbox :

1508071367.png

Nota bene : Ce serait la même chose avec le terminal, en intégrant le chemin de lancement du script

$ python /chemin_vers_votre_script_cyclope/cyclope

1508071141.png

Une fois lancée, le script cyclope ouvre une fenêtre de choix des fichiers à ouvrir :

?img=1508072011.png

Choix d’ouvrir le dossier images, une fois le script lancée à partir du menu :

?img=1508072057.png

Exemple de visionnage d’une photo sans l’option de plein écran :

?img=1508072290.png

Traitement photo avec le logiciel Gimp :

?img=1508072395.png

?img=1508072573.png
Photo visionné en plein écran :

?img=1508072632.png

Fermeture du logiciel cyclope en clic-droit souris lors du visionnage d’une image en plein écran :

?img=1508072905.png


8 / Script cyclope à lancer avec python :
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# This application is released under the GNU General Public License 
# v3 (or, at your option, any later version). You can find the full 
# text of the license under http://www.gnu.org/licenses/gpl.txt
# By using, editing and/or distributing this software you agree to 
# the terms and conditions of this license.
#_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

# original application : eyez - !v1.0~mickyz(c)2012
# simplified to cyclope, a simple image viewer - thuban (yeuxdelibad.net) © 2015

import sys
import os
import gobject
import gtk
from gtk import gdk
import gettext

progname = "cyclope"
version = "1.5"
auteur = "Thuban" 
licence = "GPLv3" 
homepage= "https://handylinux.org"
cyclopeicon = "/usr/share/pixmaps/cyclope_icon.png"
minimal_w = 420
minimal_h = 180
screen_ratio = 0.85

EXT = ('.bmp','.ico','.gif','.jpg','.jpeg','.png','.svg','.svgz','.tif','.tiff')
editors = [{'app' :'gimp', 'name' :'gimp'},\
        {'app' :'lodraw', 'name' :'Libreoffice Draw'},
        {'app' :'gthumb', 'name' :'gthumb'},\
        {'app' :'gpaint', 'name' :'gpaint'},\
        {'app' :'shotwell', 'name' :'shotwell'}]

gettext.bindtextdomain(progname, '/usr/share/locale')
gettext.textdomain(progname)
_ = gettext.gettext

def listFiles(directory):
    files = [ f for f in sorted(os.listdir(directory)) if os.path.splitext(f.lower())[1] in EXT ]
    return(files)

def help():
    print("usage : {} /path/to/picture".format(sys.argv[0]))

def error_dlg(error):
    m = gtk.MessageDialog(type=gtk.MESSAGE_ERROR,\
        buttons = gtk.BUTTONS_OK)
    m.set_markup(_("""Error while opening this file. Sorry \n {}""").format(error))
    ret = m.run()
    if ret == gtk.RESPONSE_DELETE_EVENT or ret == gtk.RESPONSE_OK:
        m.destroy()
    sys.exit(1)

def select_image():
    """return choosed filename or None"""
    chooser = gtk.FileChooserDialog(title=_("Open a picture"),action=gtk.FILE_CHOOSER_ACTION_OPEN,\
                buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
    chooser.set_current_folder(os.getcwd())
    filter = gtk.FileFilter()
    filter.set_name(_("Images"))
    mimes = ["image/png", "image/jpeg", "image/gif"]
    for m in mimes:
        filter.add_mime_type(m)
    patterns = [ "*.bmp", "*.ico", "*.gif", "*.jpg", "*.jpeg", "*.png",\
            "*.svg", "*.svgz", "*.tif", "*.tiff" ]
    for p in patterns :
        filter.add_pattern(p)
    chooser.add_filter(filter)

    response = chooser.run()
    if response == gtk.RESPONSE_OK:
        i = chooser.get_filename()
        chooser.destroy()
        return(i)
    else:
        print(_('Closed, no files selected'))
        chooser.destroy()
        return(None)

class Cyclope():
    def __init__(self, img):
        # variables
        self.imginfo = { 'path': str(), 'pixbuf': str(), 'is_static': True, 'width':0, 'height':0 }
        self.isfull = False # fullscreen or not
        self.oldtime = 0
        
        # to zoom or not to zoom
        self.zoommode = False 
        self.tozoom = False
        self.xz, self.yz, self.wz, self.hz = 0, 0, 0, 0

        self.diapomode = False

        self.image = gtk.Image() # L'image affichée

        # La fenêtre principale
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_position(gtk.WIN_POS_CENTER_ALWAYS)
        self.window.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#222222")) # Fond sombre

        # La taille de l'écran
        self.sw,self.sh = gdk.screen_width() *screen_ratio, gdk.screen_height() *screen_ratio

        # Les boutons de la fenêtre
        self.rightarrow = gtk.Button(stock=gtk.STOCK_GO_FORWARD)
        self.leftarrow = gtk.Button(stock=gtk.STOCK_GO_BACK)
        self.fullbtn = gtk.Button(stock=gtk.STOCK_FULLSCREEN)
        self.quitbtn = gtk.Button(stock=gtk.STOCK_QUIT)

        self.rightarrow.connect("button_release_event", lambda x,y: self.skipImage(+1))
        self.leftarrow.connect("button_release_event", lambda x,y: self.skipImage(-1))
        self.fullbtn.connect("button_release_event", lambda x,y: self.toggle_fullscreen())
        self.quitbtn.connect("button_release_event", lambda x,y: self.quit_app())

        self.buttons = gtk.HBox(True,0)
        self.buttons.pack_start(self.leftarrow,False, True,0)
        self.buttons.pack_start(self.rightarrow, False,True,0)
        self.buttons.pack_start(self.fullbtn, False,True,0)
        self.buttons.pack_start(self.quitbtn, False,True,0)

        for i in self.buttons: # disable keyboard focus
            if type(i) == gtk.Button: i.set_can_focus(False)

        # boite contenant l'image
        self.evbox = gtk.EventBox()
        self.evbox.add(self.image)
        self.evbox.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse('#222222')) # fond sombre
        # pour récupérer les informations nécessaires au zoom
        self.evbox.connect("button_release_event", self.apply_zoom)
        self.evbox.connect("motion_notify_event", self.motion_notify_event)
        
        # pour pas que la evbox prenne toute la place
        imgbox = gtk.HBox(False,0)
        imgbox.pack_start(self.evbox,True,False,0)
        
        # La boite qui contient tout
        self.container = gtk.VBox(False,0)
        self.container.pack_start(imgbox, True, False,0)
        self.container.pack_start(self.buttons, False, False, 0)

        # configuration de la fenêtre
        self.window.add(self.container)
        self.window.set_border_width(4) # pour avoir une bordure 
        self.window.set_decorated(False)
        if os.path.isfile(cyclopeicon):
            self.window.set_icon_from_file(cyclopeicon)
        
        self.window.add_events(gdk.BUTTON_PRESS_MASK | gdk.KEY_PRESS_MASK | gdk.SCROLL_MASK)
        self.window.connect('button_press_event', self.onButtonPress)
        self.window.connect('key_release_event', self.onKeyPress)
        self.window.connect('delete_event', self.quit_app)

        self.window.show_all()
        self.set_image(img)

        gtk.main()

        
    def quit_app(self):
        self.window.hide()
        gtk.main_quit()
        return(True)
    
    def diaporama(self):
        if not self.diapomode:
            self.diapomode = True
            gobject.timeout_add(2000, self.diapo_cycle)
        else:
            self.diapomode = False

    def diapo_cycle(self):
        if self.diapomode:
            self.skipImage(+1)
            gobject.timeout_add(3000, self.diapo_cycle)

    def back_to_nomal(self):
        self.set_image(self.imginfo['path'])
        self.updateimage()

    def toggle_zoom(self):
        if self.zoommode == True:
            self.zoommode = False
            self.evbox.window.set_cursor(None) 
            self.back_to_nomal()
        else:
            self.zoommode = True
            watch = gtk.gdk.Cursor(gtk.gdk.CROSS)
            self.evbox.window.set_cursor(watch)      
    
    def apply_zoom(self, widget, event):
        # on appuie sur entrée pour valider le zoom
        if self.zoommode and self.imginfo['is_static'] and self.tozoom : # pas besoin d'aller plus loin sinon
            # dimensions de la grande image
            oldw, oldh = self.imginfo['width'], self.imginfo['height']
            
            pix=self.image.get_pixbuf() # on récupère l'image actuelle
            zoomed_pix = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, self.wz, self.hz)
            pix.copy_area(self.xz, self.yz, self.wz, self.hz, zoomed_pix, 0, 0)  # on garde que la zone souhaitée

            # on met l'image aux dimensions de la fenêtre précédente
            ow,oh = self.sw, self.hz*self.sw/self.wz

            zoomed_pix = zoomed_pix.scale_simple(int(ow),int(oh), gdk.INTERP_BILINEAR)
            # mise à jour des informations de l'image
            self.imginfo['pixbuf'] = zoomed_pix
            self.imginfo['width'], self.imginfo['height'] = zoomed_pix.get_width(), zoomed_pix.get_height()
            
            self.image.set_from_pixbuf(zoomed_pix)
            
            self.updateimage()
            self.tozoom = False

            del pix
            del zoomed_pix            
                
    def motion_notify_event(self, widget, event):
        if self.zoommode and self.imginfo['is_static']: # pas besoin d'aller plus loin sinon
            # dimensions de la grande image
            oldw, oldh = self.imginfo['width'], self.imginfo['height']
            # récupération des données
            x, y = int(event.x), int(event.y)
            
            # attention au dépassements
            if x > oldw : x = oldw
            if x < 0 : x = 0
            if y > oldh : y = oldh
            if y < 0 : y = 0
            
            if x > self.xz_orig:
                self.wz = x - self.xz_orig
                self.xz = self.xz_orig
            else:
                self.wz = abs(x - self.xz_orig)
                self.xz = x

            if y > self.yz_orig:
                self.hz = y - self.yz_orig
                self.yz = self.yz_orig
            else:
                self.hz = abs(y - self.yz_orig)
                self.yz = y
            
            state = event.state
        
            if state & gtk.gdk.BUTTON1_MASK:
                cr = self.image.window.cairo_create()
                cr.set_source_pixbuf(self.image.get_pixbuf(),0,0) # pour effacer l'ancien rect
                cr.paint()
                cr.stroke()
                cr.set_source_rgba(0.25, 0.69, 1, 0.5)
                cr.rectangle(self.xz, self.yz, self.wz, self.hz)
                cr.fill()
                self.tozoom = True
    
    def set_image(self, img):
        """ Render the Pixbuf and show the image """
        self.get_infos(img)

        self.updateimage()

        # Quelques infos dans le titre de la fenêtre
        self.window.set_title('{} ! {}x{}'.format(\
                os.path.basename(img), self.imginfo['width'], self.imginfo['height']))

    def get_infos(self, img):
        """put image path, pixbuf and bool_static in self.imginfo"""
        if img.endswith('.gif'):
            pix = gdk.PixbufAnimation(img)
            if pix.is_static_image():
                self.imginfo = { 'path': '', 'pixbuf': '', 'is_static': True, 'width':0, 'height':0 }
                self.imginfo['path'] = img
                self.imginfo['pixbuf'] = pix.get_static_image().apply_embedded_orientation()
                self.imginfo['is_static'] = True
            else:
                self.imginfo['path'] = img
                self.imginfo['pixbuf'] = pix
                self.imginfo['is_static'] = False
        else:
            self.imginfo['path'] = img
            self.imginfo['pixbuf'] = gdk.pixbuf_new_from_file(img).apply_embedded_orientation()
            self.imginfo['is_static'] = True

        self.imginfo['width'] = self.imginfo['pixbuf'].get_width()
        self.imginfo['height'] = self.imginfo['pixbuf'].get_height()

    def updateimage(self):
        gap = self.buttons.get_allocation().height # taille des boutons à réserver        
        pix = self.imginfo['pixbuf']
        iw,ih = self.imginfo['width'], self.imginfo['height']
        
        # on cherche la taille de l'image la plus adaptée
        ow,oh = iw,ih
        if not self.isfull:
            if iw > self.sw: 
                ow,oh = self.sw, ih*self.sw/iw 
            if oh > self.sh: 
                ow,oh = iw*self.sh/ih, self.sh
        else:
            if iw > self.sw or ih > self.sh:
                ow,oh = self.sw/screen_ratio, ih*self.sw/screen_ratio/iw - gap
                if oh > self.sh: 
                    ow,oh = iw*self.sh/screen_ratio/ih, self.sh/screen_ratio - gap

        # on redimensionne l'image
        if self.imginfo['is_static']:
            pix = pix.scale_simple(int(ow),int(oh), gdk.INTERP_BILINEAR)
            self.image.set_from_pixbuf(pix)
        else:
            self.image.set_from_animation(pix)
        del pix

        # on change la taille de la fenêtre
        if self.isfull:
            self.window.resize(gdk.screen_width(),gdk.screen_height())
        else:
            if ow < minimal_w:
                self.window.resize(minimal_w, int(oh+gap))
            else:
                self.window.resize(int(ow),int(oh+gap))
                
    def toggle_fullscreen(self):
        if self.isfull: # on passe en taille normale
            self.window.unfullscreen()
            self.buttons.show()
            self.isfull = False
            self.updateimage()
            
            self.fullbtn.set_label(_("Fullscreen"))
            image = gtk.Image()
            image.set_from_stock(gtk.STOCK_FULLSCREEN, gtk.ICON_SIZE_MENU)
            self.fullbtn.set_image(image)

        else:# On passe en plein écran
            self.isfull = True
            self.window.fullscreen()
            self.buttons.hide()
            self.updateimage()

            self.fullbtn.set_label(_("Restore screen"))
            image = gtk.Image()
            image.set_from_stock(gtk.STOCK_UNDO, gtk.ICON_SIZE_MENU)
            self.fullbtn.set_image(image)

    def open_image(self):
        i = select_image()
        if i :
            self.set_image(i)

    def onButtonPress(self, widget, event):
        if self.zoommode and self.imginfo['is_static']: # pas besoin d'aller plus loin sinon
            if event.button == 1:
                self.xz_orig, self.yz_orig = int(event.x), int(event.y)
        else:
            if event.button == 1 :
                if event.type == gdk._2BUTTON_PRESS: # double clic gauche
                    self.toggle_fullscreen()
                elif not self.isfull and (event.get_time() - self.oldtime > 500 ):
                    self.window.begin_move_drag(event.button, int(event.x_root), int(event.y_root), event.time)
                    self.oldtime = event.get_time()
        if event.button == 3 or event.button == 2: # n'importe quel autre bouton
            self.onMenuPopup(event.get_time())
            

    def onKeyPress(self, win, event):
        key = gdk.keyval_name(event.keyval)
        if key == 'Escape' or key == 'q':
            if not self.isfull:
                self.quit_app()
            else:
                if self.diapomode :
                    self.diapomode = False
                self.toggle_fullscreen()
        elif key == 'p':
            self.diaporama()
        elif key == 'z':
            self.toggle_zoom()
        elif key == 'equal':
            self.back_to_nomal()
        elif key == 'f':
            self.toggle_fullscreen()
        elif key == 'Right' or key == 'n':
            self.skipImage(+1)
        elif key == 'Left' or key == 'b':
            self.skipImage(-1)
        elif key == 'o': 
            self.open_image()
        elif key == 'm': 
            self.onMenuPopup(event.get_time())
        elif key == 'd': 
            self.rotate(90)
        elif key == 'g': 
            self.rotate(-90)
        elif key == 'h': 
            self.window.set_title("Handylinux rocks!")
        elif key == 'a': 
            self.window.set_title("arpinux needs beers!")

    def onMenuPopup(self, time=0):
        self.app_menu = self.set_menu()
        self.app_menu.show_all()
        self.app_menu.popup(None, None, None, 3, time, 0)

    def set_menu(self):
        menu = gtk.Menu()

        op = gtk.ImageMenuItem(gtk.STOCK_OPEN)
        op.connect('activate', lambda w: self.open_image())
        menu.append(op)
        menu.append(gtk.SeparatorMenuItem())

        rotater = gtk.ImageMenuItem(gtk.STOCK_CONVERT)
        rotater.set_label(_("Rotate right"))
        rotater.connect('activate', lambda w: self.rotate(90))
        menu.append(rotater)

        rotatel = gtk.ImageMenuItem(gtk.STOCK_CONVERT)
        rotatel.set_label(_("Rotate left"))
        rotatel.connect('activate', lambda w: self.rotate(-90))
        menu.append(rotatel)
        
        zoom = gtk.ImageMenuItem(gtk.STOCK_ZOOM_IN)
        if self.zoommode :
            zoom.set_label(_("Disable zoom"))
            zoom.connect('activate', lambda w: self.toggle_zoom())
        else:
            zoom.set_label(_("Zoom"))
            zoom.connect('activate', lambda w: self.toggle_zoom())
        menu.append(zoom)
        
        if self.diapomode:
            diapo = gtk.ImageMenuItem(gtk.STOCK_MEDIA_STOP)
            diapo.set_label(_("Disable slideshow"))
        else:
            diapo = gtk.ImageMenuItem(gtk.STOCK_MEDIA_PLAY)
            diapo.set_label(_("Enable slideshow"))
        diapo.connect('activate', lambda w: self.diaporama())
        menu.append(diapo)
        
        for editor in editors:
            if os.path.exists('/usr/bin/{}'.format(editor['app'])):
                edit = gtk.ImageMenuItem(gtk.STOCK_EDIT)
                edit.set_label(_("Edit with {}").format(editor['name']))
                edit.connect('activate', self.openWith, editor['app'])
                menu.append(edit)

        menu.append(gtk.SeparatorMenuItem())
        apropos = gtk.ImageMenuItem(gtk.STOCK_HELP)
        apropos.connect('activate', self.about)
        menu.append(apropos)
        close = gtk.ImageMenuItem(gtk.STOCK_CLOSE)
        close.connect('activate', lambda w: self.quit_app())
        menu.append(close)

        return menu

    def skipImage(self, position):
        path = self.imginfo['path']
        d, f = os.path.dirname(path), os.path.basename(path)
        files = listFiles(d)
        for n,i in enumerate(files):
            if f == i:
                try: f = files[n+position]
                except: f = files[0]
                self.set_image(os.path.join(d, f))
                break

    def rotate(self,angle):
        pix = self.imginfo['pixbuf']
        if angle == 90:
            rotated = pix.rotate_simple(gtk.gdk.PIXBUF_ROTATE_CLOCKWISE)
        elif angle == -90:
            rotated = pix.rotate_simple(gtk.gdk.PIXBUF_ROTATE_COUNTERCLOCKWISE)

        self.imginfo['pixbuf'] = rotated
        self.imginfo['width'], self.imginfo['height'] = rotated.get_width(), rotated.get_height()

        if self.imginfo['is_static']:
            self.image.set_from_pixbuf(rotated)
        else:
            self.image.set_from_animation(rotated)
        self.updateimage()
        del pix
        del rotated

    def openWith(self, widget, data=None):
        import subprocess
        try:
            subprocess.Popen([data, self.imginfo['path']])
        except:
            print("Error at opening {} with {}".format(self.imginfo['path'], data))

    def about(self, wid=None, data=None):
        # fenêtre à propos.
        m = gtk.MessageDialog(type=gtk.MESSAGE_INFO,\
            buttons = gtk.BUTTONS_OK)
        m.set_markup(_("""<b>{}</b>
version : {}
author : {}
licence : {}
homepage : {}
---
usage :
Use arrows, b and n keys to change image
Double click or press f key to enlarge the window
Press o to open a new image
Press d or g to turn the image
Press z to toggle zoom mode
Press = to restore the image
Press p to start slideshow mode
Press m or right click to show the menu
Press Escape or q to quit""").format(progname, version, auteur, licence, homepage))
        ret = m.run()
        if ret == gtk.RESPONSE_DELETE_EVENT or ret == gtk.RESPONSE_OK:
            m.destroy()

def main():
    if len(sys.argv) == 1 :  # si aucune image en argument
        img = select_image()
        if not img :
            sys.exit()       # on quitte s'il n'y a pas d'image choisie
    elif len(sys.argv) == 2:
        img = os.path.abspath(sys.argv[1])

    if os.path.isdir(img):   # si c'est un répertoire
        imglist = sorted(os.listdir(img))
        if len(imglist) > 0: # on ouvre les images du répertoire
            img = os.path.join(img,imglist[0])

    try: 
        window = Cyclope(img) # ouverture d'une image
    except Exception as e:
        help()
        error_dlg(e)

    return 0

if __name__ == '__main__':
    main()

# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
Conclusion :

Voilà, cyclope fonctionne bien sous Fedora (n, n+1, ...).

Le principe devrait être le même quelque soit le système unix employé, y compris NetBSD et OpenBSD.

Ne sachant pas faire de paquet rpm, je me contente de cette démarche empirique, mais qui fonctionne, puisque fondée sur le principe basique que tout est script sous unix.

En vous souhaitant un bon usage du script cyclope.

Bien à vous.

Mr. S.


Sources :

- Sources.list d’Handylinux : http://repo.handylinux.org/handylinux.list
- article « fabrication de package rpm » : http://eric.gerbier.free.fr/fabrication-rpm.html,
- article « Créer un package RPM » : http://wiki.gantzer.eu/index.php/Cr%C3% … ackage_RPM,
- forum openclassroom : https://openclassrooms.com/forum/sujet/ … -sur-linux,
- fonds d’écran « Viperr 9 » : https://viperr.org/site/img/DesktopV9.png.

Hors ligne

#7 15-10-2017 16:44:53

Mr. S.
Membre

Re : Portage du paquet debian stretch "cyclope" vers fedora 26

Re-bonjour,

Les photos ne passent pas. Dommage, car j'ai suivi les instructions. Le texte s'avère toutefois plus lisible.

Je vais reformater les tutoriels que je possède en réserve.

Bien à vous.

Hors ligne

#8 15-10-2017 18:50:04

IceF0x
#! Gourou Linux

Re : Portage du paquet debian stretch "cyclope" vers fedora 26

J'ai tenté de corriger les images mais il y en as trop, tu confond url et image dans tes balises.


Utiliser des logiciels propriétaires, c'est comme les plats préparés, on est incapable de dire les conservateurs qu'ils contiennent, on dira toujours que c'est bon, mais ça ne remplacera jamais le repas fait maison par sa maman.
]:D #! Crunchbang & Archlinux GNU/Linux User ]:D

Hors ligne

#9 15-10-2017 20:19:20

Mr. S.
Membre

Re : Portage du paquet debian stretch "cyclope" vers fedora 26

Merci beaucoup IceFox.

En fait, il y a 23 photos et le forum n'en supporte que 20.

Je vais retravailler ma copie.

D'autant plus que les photos se mélangent maintenant à la sortie pour celles qui émergent du néant. Pas trop grave en soi.

Je possède une version en odt et en pdf. Je ne sais pas si cela peut être basculé sur le forum; ce serait plus pratique. Le côté positif, c'est que j'apprends des choses nouvelles !

Hors ligne

#10 15-10-2017 23:08:36

Mr. S.
Membre

Re : Portage du paquet debian stretch "cyclope" vers fedora 26

Bonjour tout le monde,

Je viens de refaire ma copie en tenant compte des remarques d'IceFox.

J'ai réduit le nombre de photos au strict minimum, mais elle n'apparaisse toujours pas. Alors tant pis, ce qui compte c'est le code !

Le texte se suffit largement à lui-même.

Bonne lecture et bon usage !

#################

Edit: j'ai mis le tuto sur ton premier post pour plus de lisibilité.

Hors ligne

#11 16-10-2017 08:59:44

penthium2
Modérateur

Re : Portage du paquet debian stretch "cyclope" vers fedora 26

hA là ca donne envis !

Merci pour ton taf, je lirais cela a tete reposé.

Merci


vi est mon ami pour la vie
Viperr
Ph'nglui nglw-nafh Cthulhu R'lyeh wgah-nagl fhtagn

Hors ligne

#12 16-10-2017 19:51:19

ZeR0-@bSoLu
Membre

Re : Portage du paquet debian stretch "cyclope" vers fedora 26

bon taf et bonne détermination continu comme ça smile


Mess With The Bests
Die Like The Rest

Hors ligne

Pied de page des forums