windows – Sam & Max http://sametmax.com Du code, du cul Wed, 30 Oct 2019 15:34:04 +0000 en-US hourly 1 https://wordpress.org/?v=4.9.7 32490438 Gérer plusieurs versions de Python avec “py” sous Windows http://sametmax.com/gerer-plusieurs-versions-de-python-avec-py-sous-windows/ http://sametmax.com/gerer-plusieurs-versions-de-python-avec-py-sous-windows/#comments Sat, 10 Jan 2015 09:10:27 +0000 http://sametmax.com/?p=15699 py sous Windows, qui permet de choisir quelle version de Python on lance.]]> Il est courant de vouloir installer plusieurs versions de Python. Pour faire des tests, pour s’assurer que son code est portable, pour utiliser des libs qui marchent uniquement sur une des versions, etc.

Sous Linux, c’est facile : chaque interpretteur est préfixé. Par exemple, sous Ubuntu, si je veux utiliser Python 3.4, je l’installe :

sudo apt-get install python3.4

Et si je lance python, ça me lance la 2.7 car c’est celle de base. Mais si je lance python3.4, ça me lance bien la 3.4.

Au final, on finit par utiliser des environnements virtuels qui isolent des versions de Python particulières. Mais ça ne retire pas l’envie de pouvoir choisir sa version de Python au niveau du système, ce qui reste difficile à faire sous Windows.

Or, depuis la version 3.3, l’installeur pour cet OS de Python installe la commande py, qui permet de choisir quelle version de Python on lance.

Dans un terminal

Si dans une console vous faites :

py

Il lancera le shell de votre installation Python 2.x la plus récente.

Si vous faites :

py script.py

Il exécutera le script avec votre installation Python 2.x la plus récente.

Mais vous pouvez passez un flag -version pour forcer une version de Python. Lancer le shell Python avec la 3.3 :

py -3.3

Lancer un script avec la 3.4 :

py -3.4 script.py

Cela suppose que vous avez la 3.3 et la 3.4, installés, évidement.

La syntaxe est surprenante. J’aurais pensé qu’ils mettraient un truc du genre py -i 3.4 mais non, c’est direct -numero.

La commande py accepte aussi les paramètres qu’on passerait normalement à la commande python, et notament l’option -m module, qui permet de lancer un module en particulier.

C’est pratique pour lancer ipython ou pip avec une version particulière. Par exemple pour installer autobahn uniquement pour la version 3.4 et donc utiliser asyncio :

py -3.4 -m pip install autobahn

Bang !

La commande py reconnait également la ligne shebang, cette syntaxe unix qui dit quel interpretteur utiliser. Si vous mettez sur la première ligne de votre script :

#! python3.4

Alors :

py script.py

Invoquera python 3.4.

La commande est capable de se débrouiller avec les chemins Unix, afin de garder la portabilité. Donc si vous faites :

#! /usr/bin/env python2.7

Alors :

py script.py

Va ignorer le debut de la ligne, et prendre l’installation locale de Python 2.7 pour lancer le script.

On click

Si vous installez la version Python 3.3 ou 3.4 en premier, les fichiers .py seront associés à la commande py. Donc si vous cliquez sur un script Python avec une ligne shebang, la bonne version sera lancée.

Mais si vous avez installé Python 2.x avant, il est possible que vos fichiers .py soient encore associés directement à la commande python ordinnaire.

Pour changer cela, faites un clic droit sur un fichier .py, modifiez le programme qui ouvre ce fichier et faites le pointer sur "C:\Windows\py.exe".

]]>
http://sametmax.com/gerer-plusieurs-versions-de-python-avec-py-sous-windows/feed/ 7 15699
Créer un réseau ad hoc sous Windows 8 http://sametmax.com/creer-un-reseau-ad-hoc-sous-windows-8/ http://sametmax.com/creer-un-reseau-ad-hoc-sous-windows-8/#comments Thu, 04 Dec 2014 15:07:34 +0000 http://sametmax.com/?p=12704 On a pas toujours un switch sous la main, et pourtant, mettre deux ordinateurs en réseau peut se faire sentir. Ok, c’est pour faire une LAN, qui j’essaye de tromper là ?

La solution, c’est le réseau ad hoc, c’est à dire demander à son ordi d’être point d’accès WIFI et réseau local.

Sous Windows 7, c’est facile, panneau de config, réseaux et partages, 3 clics et ça roule.

Mais mon Windows 8 ne voyait pas le réseau, et j’ai steam installé dessus.

Du coup, on a cherché un moyen de faire le réseau depuis W8, et l’option a été retirée des GUI.

Heureusement, il reste la ligne de commande.

D’abord, vérifier que son matos supporte le mode ad hoc :

netsh wlan show drivers

Dans la liste, il doit y avoir une ligne à propos d’un réseau hébergé avec marqué “oui”.

Si ce n’est pas le cas, fin du voyage. Sinon, on configure le réseau (les droits admins sont nécessaires) :

netsh wlan set un_nom_de_reseau mode=allow ssid=un_ssid key=un_mot_de_passe

Quand on veut lancer le réseau :

netsh wlan start un_nom_de_reseau

Quand on veut l’arrêter :

netsh wlan stop un_nom_de_reseau

Et les autres systèmes peuvent le voir dans la liste des réseaux WIFI sous le nom de “un_ssid” avec le mot de passe “un_mot_de_passe”.

]]>
http://sametmax.com/creer-un-reseau-ad-hoc-sous-windows-8/feed/ 3 12704
Explication de code : python-mss http://sametmax.com/explication-de-code-python-mss/ http://sametmax.com/explication-de-code-python-mss/#comments Sat, 22 Feb 2014 14:19:02 +0000 http://sametmax.com/?p=9219 envoyez nous les codes que vous ne pigez pas". Celle-ci est un peu particulière.]]> Ca faisait longtemps qu’on avait pas eu une petite explication de code dans le cadre de notre politique “envoyez nous les codes que vous ne pigez pas“.

Celle-ci est un peu particulière.

D’abord parce qu’elle traine dans la boîte mail depuis un siècle ou deux. Je pense que l’auteur de la demande n’en a plus besoin…

Ensuite parce que le code est assez complexe, notamment à cause de l’utilisateur d’API C. Donc si vous n’avez pas des notions de C, vous n’allez rien piger. En effet je ne vais pas expliquer les bases de C ou de Python, ce n’est pas un tuto, donc il me faut des prérequis. Je vous invite quand même à vous rafraichir la mémoire en utilisant notre introduction au module ctypes car il est massivement utilisé dans ce code.

Exceptionnellement, je ne vais pas pondre de version alternative car :

  • le code est très très long, et j’ai déjà passé mon samedi matin à écrire cet article.
  • je n’ai pas de mac pour tester cette partie.

Comme d’habitude, je vais faire des remarques parfois critiques sur le code, mais mon intention n’est bien entendu pas de faire du tord à l’auteur. C’est pédagogique pour les lecteurs. D’ailleurs ce bout de code est assez impressionnant et a du demander des heures et des heures de travail tant au niveau du code que de la recherche d’information. J’insiste donc sur mon respect pour l’auteur. On est pas sur bashfr.

Je tiens tout de même à prévenir que la lib ne fonctionne pas sur ma machine, plante sur certains appels, ou produits des screenshots illisibles. Je ne doute néanmoins pas de la compétence de l’auteur, ce qu’il essaye de faire est vraiment compliqué, et ne pense que Python n’est pas son premier langage.

On m’a signalé dans le twitcoutuer qu’il manque de la zik :

Normalement le but de la lib est de permettre de faire des screenshots en pure Python sous Windows, Linux et Mac. Exemple de code sous Linux :

>>> from mss import MSSLinux
>>> mss = MSSLinux()
>>> screnshots = mss.save(output='/tmp/screenshots', oneshot=True)
>>> list(screnshots)
[u'/tmp/screenshots-full.png']

Et voici le code commenté. C’est un gros morceau, et bien complexes, avec des notions parfois que je ne maitrise pas. J’ai pu faire des erreurs et dire des bêtises. Si l’auteur passe par là, il a bien entendu un droit de réponse.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

''' A cross-platform multi-screen shot module in pure python using ctypes.

    This module is maintained by Mickaël Schoentgen .
    If you find problems, please submit bug reports/patches via the
    GitHub issue tracker (https://github.com/BoboTiG/python-mss/issues).

    Note: please keep this module compatible to Python 2.6.

    Still needed:
    * support for additional systems

    Many thanks to all those who helped (in no particular order):

      Oros, Eownis

    History:

    

    0.0.1 - first release
    0.0.2 - add support for python 3 on Windows and GNU/Linux
    0.0.3 - MSSImage: remove PNG filters
          - MSSImage: remove 'ext' argument, using only PNG
          - MSSImage: do not overwrite existing image files
          - MSSImage: few optimizations into png()
          - MSSLinux: few optimizations into get_pixels()
    0.0.4 - MSSLinux: use of memoization => huge time/operations gains
    0.0.5 - MSSWindows: few optimizations into _arrange()
          - MSSImage: code simplified

    You can always get the latest version of this module at:

            https://raw.github.com/BoboTiG/python-mss/master/mss.py

    If that URL should fail, try contacting the author.
'''

from __future__ import (unicode_literals, absolute_import,
                        division, print_function)

__version__ = '0.0.5'
__author__ = "Mickaël 'Tiger-222' Schoentgen"
__copyright__ = '''
    Copyright (c) 2013, Mickaël 'Tiger-222' Schoentgen

    Permission to use, copy, modify, and distribute this software and its
    documentation for any purpose and without fee or royalty is hereby
    granted, provided that the above copyright notice appear in all copies
    and that both that copyright notice and this permission notice appear
    in supporting documentation or portions thereof, including
    modifications, that you make.
'''

# Bon je vais pas vous expliquer ce que les lignes du dessus font hein...hein


# __all__ liste les objets importables si on fait from mss import *
# ce qui limite la pollution du namespace avec tout un tas de choses inutiles
# comme pack, isfile, system, etc. qui sont quelques lignes plus bas.
__all__ = ['MSSImage', 'MSSLinux', 'MSSMac', 'MSSWindows']

# Permet de trouve une bibliothèqe via son nom sur le système en cherchant
# divers chemins standard à l'OS
from ctypes.util import find_library

# Permet une forme de sérialisation des types simples qui est compatible
# entre Python et C
from struct import pack

# Permet de vérifier si un fichier existe et n'est pas un dossier
from os.path import isfile

# Permet de récupérer le nom de l'OS
from platform import system

# Divers opérations sur le système
import sys

# Compression zip
import zlib

# On importe conditionnellement des packages selon le système sur lequel on
# est. La raison de cela est que certains packages n'existe pas (ou ne sont pas
# utiles) sur certains OS.

# Darwin, c'est l'OS open source qui sert de base au Mac. C'est
# un mélange de NeXTSTEP et de FreeBSD.
if system() == 'Darwin':
    # Quartz est la techno derrière l'affichage des Mac incluant notament
    # le compositeur graphique et moteur de rendu 2D. Il a un binding Python
    # intégré puisque les Mac utilisent Python un peu partout. A ce demander
    # pourquoi ces neuneus nous ont collé objectifs C pour le dev.
    from Quartz import *
    # On importe juste l'équivalent du mimetype sous Mac pour le PNG. C'est
    # une "constante"
    from LaunchServices import kUTTypePNG


elif system() == 'Linux':

    # Accès aux variables d'environnement
    from os import environ
    # Fonction pour transformer ~ dans les chemin d'accès en chemin vers
    # le dossier utilisateur
    from os.path import expanduser
    # Parseur de xml
    import xml.etree.ElementTree as ET

    # 'byref' permet d'obtenir un pointer sur une fonction c,
    # 'cast' est similaire à l'opérateur cast en c et permet le type casting
    # d'un objet c, 'cdll' permet de charger des shared lib C
    from ctypes import byref, cast, cdll

    # je ne sais pas pourquoi ça n'a pas été fait une seule ligne...
    # Tout ça représente les types c éponymes, mais en plus Structure est une
    # classe abstraite dont on peut hériter faire une classe qui peut être
    # passée en paramètre à une fonction C qui attend un struc.
    from ctypes import (
        c_char_p, c_int, c_int32, c_uint, c_uint32,
        c_ulong, c_void_p, POINTER, Structure
    )

    # On hérite de Structure ce qui nous fait pour le moment une structure
    # vide
    class Display(Structure):
        pass

    # Une structure représentant les attributs d'une fenêtre pour le serveur
    # d'affichage sous Linux.
    class XWindowAttributes(Structure):
        _fields_ = [
            ('x',                     c_int32),
            ('y',                     c_int32),
            ('width',                 c_int32),
            ('height',                c_int32),
            ('border_width',          c_int32),
            ('depth',                 c_int32),
            ('visual',                c_ulong),
            ('root',                  c_ulong),
            ('class',                 c_int32),
            ('bit_gravity',           c_int32),
            ('win_gravity',           c_int32),
            ('backing_store',         c_int32),
            ('backing_planes',        c_ulong),
            ('backing_pixel',         c_ulong),
            ('save_under',            c_int32),
            ('colourmap',             c_ulong),
            ('mapinstalled',          c_uint32),
            ('map_state',             c_uint32),
            ('all_event_masks',       c_ulong),
            ('your_event_mask',       c_ulong),
            ('do_not_propagate_mask', c_ulong),
            ('override_redirect',     c_int32),
            ('screen',                c_ulong)
        ]

    # structure définissant une image telle qu'elle existe dans la mémoire
    # d'un client du serveur d'affichage
    class XImage(Structure):
        _fields_ = [
            ('width'            , c_int),
            ('height'           , c_int),
            ('xoffset'          , c_int),
            ('format'           , c_int),
            ('data'             , c_char_p),
            ('byte_order'       , c_int),
            ('bitmap_unit'      , c_int),
            ('bitmap_bit_order' , c_int),
            ('bitmap_pad'       , c_int),
            ('depth'            , c_int),
            ('bytes_per_line'   , c_int),
            ('bits_per_pixel'   , c_int),
            ('red_mask'         , c_ulong),
            ('green_mask'       , c_ulong),
            ('blue_mask'        , c_ulong)
        ]

    # Apparement la fonction pack avec ce format va être appelée souvent
    # doc l'auteur se fait un raccourci. Le format en question est 'B', donc
    # du unsigned char, et '<', donc du little endian. Et là vous comprenez
    # le bonheur de travailler dans un langage de haut niveau comme Python.
    def b(x):
        return pack(b' '3':
                display = bytes(environ['DISPLAY'], 'utf-8')
            else:
                display = environ['DISPLAY']
        except KeyError:
            err = 'MSSLinux: $DISPLAY not set. Stopping to prevent segfault.'
            raise ValueError(err)
        self.debug('init', '$DISPLAY', display)

        # On récupère l'écran par défaut et la fenêtre racine sur l'affichage
        # en cours.

        # At this point, if there is no running server, it could end on
        # a segmentation fault. And we cannot catch it.
        self.display = self.XOpenDisplay(display)
        self.debug('init', 'display', self.display)
        self.screen = self.XDefaultScreen(self.display)
        self.debug('init', 'screen', self.screen)
        self.root = self.XDefaultRootWindow(self.display, self.screen)
        self.debug('init', 'root', self.root)

    # les deux méthodes qui servent à manuellement définir les types
    # des autres méthodes dont j'ai parlé plus haut.

    def _set_argtypes(self):
        ''' Functions arguments '''

        self.debug('_set_argtypes')

        self.XOpenDisplay.argtypes = [c_char_p]
        self.XDefaultScreen.argtypes = [POINTER(Display)]
        self.XDefaultRootWindow.argtypes = [POINTER(Display), c_int]
        self.XGetWindowAttributes.argtypes = [POINTER(Display),
            POINTER(XWindowAttributes), POINTER(XWindowAttributes)]
        self.XAllPlanes.argtypes = []
        self.XGetImage.argtypes = [POINTER(Display), POINTER(Display),
            c_int, c_int, c_uint, c_uint, c_ulong, c_int]
        self.XGetPixel.argtypes = [POINTER(XImage), c_int, c_int]
        self.XFree.argtypes = [POINTER(XImage)]
        self.XCloseDisplay.argtypes = [POINTER(Display)]

    def _set_restypes(self):
        ''' Functions return type '''

        self.debug('_set_restypes')

        self.XOpenDisplay.restype = POINTER(Display)
        self.XDefaultScreen.restype = c_int
        self.XDefaultRootWindow.restype = POINTER(XWindowAttributes)
        self.XGetWindowAttributes.restype = c_int
        self.XAllPlanes.restype = c_ulong
        self.XGetImage.restype = POINTER(XImage)
        self.XGetPixel.restype = c_ulong
        self.XFree.restype = c_void_p
        self.XCloseDisplay.restype = c_void_p


    def enum_display_monitors(self):
        ''' Get positions of one or more monitors.
            Returns a dict with minimal requirements (see MSS class).
        '''

        self.debug('enum_display_monitors')

        results = []
        if self.oneshot:
            # Dans le cas d'un seul screenshot pour tout, on récupère juste
            # les coordonnées de la fenêtre racine.
            gwa = XWindowAttributes()
            self.XGetWindowAttributes(self.display, self.root, byref(gwa))
            results.append({
                b'left'  : int(gwa.x),
                b'top'   : int(gwa.y),
                b'width' : int(gwa.width),
                b'height': int(gwa.height)
            })
        else:

            # Sinon on parse le fichier XML de config pour essayer de
            # trouver les coordonnées. Je ne suis pas certain que ce soit
            # une bonne stratégie puisque le fichier monitors.xml contient
            # tous les moniteurs jamais branché sur la machine, y compris ceux
            # qu'on a pas branché depuis 1000 ans...

            # It is a little more complicated, we have to guess all stuff
            # from ~/.config/monitors.xml, if present.
            monitors = expanduser('~/.config/monitors.xml')
            if not isfile(monitors):
                # Ici, lever une exception serait pas mal.
                # A la place, l'auteur choisit de mettre oneshot et de relancer
                # la capture. Donc au lieu d'avoir ce qu'on demande ou une
                # erreur, on a ce qu'on demande ou ce qu'on ne demande pas.
                # Encore une fois, j'en profite pour souligner qu'il faut
                # éviter ce genre de config à base de site effects. Devoir
                # setter un attribut pour avoir un résultat différent à cette
                # méthode n'est pas très propre.
                self.debug('ERROR', 'MSSLinux: enum_display_monitors() failed (no monitors.xml).')
                self.oneshot = True
                return self.enum_display_monitors()

            # Le XML est une collection de noeuds 'configuration' qui représentent
            # chacun un moniteur. On récupère ici le premier noeud 'configurations'
            tree = ET.parse(monitors)
            root = tree.getroot()
            config = root.findall('configuration')[-1]
            conf = []
            # chaque noeud "configurations" à une série de noeuds ouput qui
            # représentent chaque format de sortie (VGA, HDMI, etc). On
            # boucle dessus.
            for output in config.findall('output'):
                name = output.get('name')
                if name != 'default':
                    # On récupère les coordonnées, la rotation, on extrait,
                    # on corrige, on ajoute à la liste... Bref, même topo
                    # qu'avec MacOsX.
                    x = output.find('x')
                    y = output.find('y')
                    width = output.find('width')
                    height = output.find('height')
                    rotation = output.find('rotation')
                    if None not in [x, y, width, height] and name not in conf:
                        conf.append(name)
                        if rotation.text in ['left', 'right']:
                            width, height = height, width
                        results.append({
                            b'left'    : int(x.text),
                            b'top'     : int(y.text),
                            b'width'   : int(width.text),
                            b'height'  : int(height.text),
                            b'rotation': rotation.text
                        })
        return results

    def get_pixels(self, monitor):
        ''' Retreive all pixels from a monitor. Pixels have to be RGB.
        '''

        self.debug('get_pixels')

        # On récupère les coordonnées des monitors revoyées par enum_display_monitors
        width, height = monitor[b'width'], monitor[b'height']
        left, top = monitor[b'left'], monitor[b'top']
        ZPixmap = 2

        # On récupère un masque de pixels pour l'ensemble de l'affichage
        allplanes = self.XAllPlanes()
        self.debug('get_pixels', 'allplanes', allplanes)

        # Visiblement un fix. C'est ce qu'on appelle un commentaire utile.
        # Fix for XGetImage: expected LP_Display instance instead of LP_XWindowAttributes
        root = cast(self.root, POINTER(Display))

        # On récupère un dump des pixels pour les coordonnées en cours, de
        # l'affichage en cours, on lui applique le masque de pixel mais
        # je ne sais pas pourquoi on doit le faire. x11 est une bestiole
        # très tarabiscotée, et mes recherches n'ont rien donné. L'auteur
        # a du bien s'amuser à trouver comment faire ce genre de chose.
        image = self.XGetImage(self.display, root, left, top, width,
            height, allplanes, ZPixmap)
        if image is None:
            raise ValueError('MSSLinux: XGetImage() failed.')

        # Les pixels doivent être récupérés en RGB. L'auteur fait donc une
        # fonction de conversion (c'est une fonction inline, donc jetable)
        # puis l'applique à la liste des pixels qu'il récupère dans l'image.
        # Une simple boucle for aurait fait l'affaire mais l'auteur a mis
        # en place une stratégie de mémoisation (mise en cache) dans la fonction.
        # Vu que la fonction est inline, un simple dico aurait aussi fait
        # l'affaire. Mais une il est très possible qu'on lui ait donné
        # le truc et comme il n'est pas habitué à Python il a juste copié/collé.
        # Je le fais souvent en Java / C donc je vais pas lui jeter la pierre.
        def pix(pixel, _resultats={}):
            ''' Apply shifts to a pixel to get the RGB values.
                This method uses of memoization.
            '''
            # La mise en cache se fait à ce niveau.
            if not pixel in _resultats:
                # Là c'est du byte shifting, mais quelle logique exacte est
                # implémentée, aucune idée. Il faudrait regarder les algos
                # de conversion pixels vers RGB et trouver celui qui est
                # appliqué. C'est le genre de truc que je copie/colle car
                # je suis trop feignant et tester que ça marche est plus rapide
                # que comprendre.
                _resultats[pixel] = b((pixel & 16711680) >> 16) + b((pixel & 65280) >> 8) + b(pixel & 255)
            return _resultats[pixel]

        # Aliasing de la fonction pour gagner en vitesse en évitant un lookup
        # d'attribut dans une boucle.
        get_pix = self.XGetPixel
        # Boucle de conversion via une liste en intention.
        pixels = [pix(get_pix(image, x, y)) for y in range(height) for x in range(width)]

        # Ici get_pixels retourne l'objet image plutôt que de la sauver
        # dans un attribut self.image. Ouch.
        self.XFree(image)
        return b''.join(pixels)

# On passe maintenant à l'implémentation pour Windows

class MSSWindows(MSS):
    ''' Mutli-screen shot implementation for Microsoft Windows. '''

    # Même topo que pour la version Linux. Même principe que son init.
    def init(self):
        ''' Windows initialisations '''

        self.debug('init')

        self.GetSystemMetrics = windll.user32.GetSystemMetrics
        self.EnumDisplayMonitors = windll.user32.EnumDisplayMonitors
        self.GetWindowDC = windll.user32.GetWindowDC
        self.CreateCompatibleDC = windll.gdi32.CreateCompatibleDC
        self.CreateCompatibleBitmap = windll.gdi32.CreateCompatibleBitmap
        self.SelectObject = windll.gdi32.SelectObject
        self.BitBlt = windll.gdi32.BitBlt
        self.GetDIBits = windll.gdi32.GetDIBits
        self.DeleteObject = windll.gdi32.DeleteObject

        self._set_argtypes()
        self._set_restypes()

    def _set_argtypes(self):
        ''' Functions arguments '''

        self.debug('_set_argtypes')

        self.MONITORENUMPROC = WINFUNCTYPE(INT, DWORD, DWORD,
            POINTER(RECT), DOUBLE)
        self.GetSystemMetrics.argtypes = [INT]
        self.EnumDisplayMonitors.argtypes = [HDC, LPRECT,
            self.MONITORENUMPROC, LPARAM]
        self.GetWindowDC.argtypes = [HWND]
        self.CreateCompatibleDC.argtypes = [HDC]
        self.CreateCompatibleBitmap.argtypes = [HDC, INT, INT]
        self.SelectObject.argtypes = [HDC, HGDIOBJ]
        self.BitBlt.argtypes = [HDC, INT, INT, INT, INT, HDC, INT, INT, DWORD]
        self.DeleteObject.argtypes = [HGDIOBJ]
        self.GetDIBits.argtypes = [HDC, HBITMAP, UINT, UINT, LPVOID,
            POINTER(BITMAPINFO), UINT]

    def _set_restypes(self):
        ''' Functions return type '''

        self.debug('_set_restypes')

        self.GetSystemMetrics.restypes = INT
        self.EnumDisplayMonitors.restypes = BOOL
        self.GetWindowDC.restypes = HDC
        self.CreateCompatibleDC.restypes = HDC
        self.CreateCompatibleBitmap.restypes = HBITMAP
        self.SelectObject.restypes = HGDIOBJ
        self.BitBlt.restypes =  BOOL
        self.GetDIBits.restypes = INT
        self.DeleteObject.restypes = BOOL

    def enum_display_monitors(self):
        ''' Get positions of one or more monitors.
            Returns a dict with minimal requirements (see MSS class).
        '''

        self.debug('enum_display_monitors')

        # le code qui permet de récupérer les moniteurs est visiblement
        # asynchrone, donc on fabrique un callback qui va remplir le tableau
        # des résultats.

        def _callback(monitor, dc, rect, data):
            rct = rect.contents
            results.append({
                b'left'  : int(rct.left),
                b'top'   : int(rct.top),
                b'width' : int(rct.right - rct.left),
                b'height': int(rct.bottom -rct.top)
            })
            return 1

        results = []
        # si c'est juste un seul screenshot, c'est un appel synchrone
        if self.oneshot:
            # ce sont des constantes qui déterminent quelle info ont veut que
            # GetSystemMetrics renvoit. Ici on demande les coordonnées de tout
            # l'écran, une par une.
            SM_XVIRTUALSCREEN = 76
            SM_YVIRTUALSCREEN = 77
            SM_CXVIRTUALSCREEN = 78
            SM_CYVIRTUALSCREEN = 79
            left = self.GetSystemMetrics(SM_XVIRTUALSCREEN)
            right = self.GetSystemMetrics(SM_CXVIRTUALSCREEN)
            top = self.GetSystemMetrics(SM_YVIRTUALSCREEN)
            bottom = self.GetSystemMetrics(SM_CYVIRTUALSCREEN)
            results.append({
                b'left'  : int(left),
                b'top'   : int(top),
                b'width' : int(right - left),
                b'height': int(bottom - top)
            })
        else:
            # On enrobe le callback Python dans un proxy qui le rend
            # utilisable par le code C
            callback = self.MONITORENUMPROC(_callback)
            # On demande à windows de nous lister les moniteurs, et comme
            # cet appel est asynchrone, on lui passe un callback pour qu'il
            # replisse la liste au fur et à mesure
            self.EnumDisplayMonitors(0, 0, callback, 0)

        # On retourne la liste. A ce stade là, on ne sait pas si la liste
        # est vide, à moitié remplie ou complètement remplie. Utiliser du
        # code asynchrone au milieu d'une lib synchrone, c'est assez dangereux
        # donc je me demande comment il retombe sur ses pieds.
        return results

    # A ce stade là il est 13h, j'ai commencé à 9h et j'ai la dalle. J'en ai
    # marre de commenter ce code. 'envoyez-vous les codes que vous pigez pas'
    # est une idée à la con. Je vous hais tous. Mon coloc va me ramener des
    # frites.

    def get_pixels(self, monitor):
        ''' Retreive all pixels from a monitor. Pixels have to be RGB. '''

        self.debug('get_pixels')

        # Encore une fois on récupère les coordonnées des moniteurs filées
        # par enum_display_monitors
        width, height = monitor[b'width'], monitor[b'height']
        left, top = monitor[b'left'], monitor[b'top']

        # Reajustement de la taille de la largeur. Je suppose que c'est
        # empirique, mais je peux me tromper.
        good_width = (width * 3 + 3) & -4
        # Valeur de paramètre qui dit de copier directement dans le rectangle
        # des destination.
        SRCCOPY = 0xCC0020
        DIB_RGB_COLORS = 0

        # Récupère le Device Context (title bar, menus, and scroll bars, etc)
        srcdc = self.GetWindowDC(0)
        # On fabrique une copie en mémoire.
        memdc = self.CreateCompatibleDC(srcdc)
        # On fabrique un bitmap basé sur ce DC
        bmp = self.CreateCompatibleBitmap(srcdc, width, height)
        # On injecte un bitmap dans le DC en mémoire.
        self.SelectObject(memdc, bmp)
        # On transfert les bits d'un DC à l'autre.
        self.BitBlt(memdc, 0, 0, width, height, srcdc, left, top, SRCCOPY)
        # On fabrique le header du BMP
        bmi = BITMAPINFO()
        bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER)
        bmi.bmiHeader.biWidth = width
        bmi.bmiHeader.biHeight = height
        bmi.bmiHeader.biBitCount = 24
        bmi.bmiHeader.biPlanes = 1
        buffer_len = height * good_width
        # On fabrique un array de char
        pixels = create_string_buffer(buffer_len)
        # On récupère les bits du bitmap issu du DC
        bits = self.GetDIBits(memdc, bmp, 0, height, byref(pixels),
            pointer(bmi), DIB_RGB_COLORS)

        self.debug('get_pixels', 'srcdc', srcdc)
        self.debug('get_pixels', 'memdc', memdc)
        self.debug('get_pixels', 'bmp', bmp)
        self.debug('get_pixels', 'buffer_len', buffer_len)
        self.debug('get_pixels', 'bits', bits)
        self.debug('get_pixels', 'len(pixels.raw)', len(pixels.raw))

        # Nettoyage des objets.
        # Clean up
        self.DeleteObject(srcdc)
        self.DeleteObject(memdc)
        self.DeleteObject(bmp)

        # Apparemment la récupération peut échouer et on peut vérifier
        # cet échec en checkant la longueur. Après la cause de l'échec est
        # un mystère
        if bits != height or len(pixels.raw) != buffer_len:
            raise ValueError('MSSWindows: GetDIBits() failed.')

        # Réorganise les bits dans le bonne ordre et converti le format
        # de couleur de BGR vers RGB

        # Note that the origin of the returned image is in the
        # bottom-left corner, 32-bit aligned. And it is BGR.
        # Need to "arrange" that.
        return self._arrange(pixels.raw, good_width, height)


    def _arrange(self, data, width, height):
        ''' Reorganises data when the origin of the image is in the
            bottom-left corner and converts BGR triple to RGB. '''

        self.debug('_arrange')

        # On crée une nouvelle liste pleine de zéro, et on la rempli
        # avec la nouvelle position des pixels.
        total = width * height
        scanlines = [b'0'] * total
        for y in range(height):
            off = width * (y + 1)
            offset = total - off
            for x in range(0, width - 2, 3):
                # On inverse aussi la position du bleu et du rouge
                scanlines[off+x:off+x+3] = b(data[offset+x+2]), b(data[offset+x+1]), b(data[offset+x])
        return b''.join(scanlines)

# Un wrapper pour dumper un array de pixels dans un fichier
# Typiquement un truc qui aurait pu tenir dans une fonction au lieu d'une classe.

class MSSImage(object):
    ''' This is a class to save data (raw pixels) to a picture file.
    '''

    def __init__(self, data=None, width=1, height=1):
        self.data = data
        self.width = int(width)
        self.height = int(height)

        if self.data is None:
            raise ValueError('MSSImage: no data to process.')
        elif self.width < 1 or self.height < 1:
            raise ValueError('MSSImage: width or height must be positive.')


    # Tout le boulot se passe ici.

    def dump(self, output):
        ''' Dump data to the image file.
            Pure python PNG implementation.
            Image represented as RGB tuples, no interlacing.
            http://inaps.org/journal/comment-fonctionne-le-png
        '''

        # On ouvre le fichier en mode écriture binaire.

        with open(output, 'wb') as fileh:
            # Pour cette partie il faut connaitre les subtilités du format PNG
            # pour comprendre, ce qui n'est pas mon cas. Donc je vais faire
            # de la déduction au gros doigt mouillé.

            # Ca prend les données en pixel, ça en fait des morceaux de la bonne taille,
            # organisés en rangés de pixels puisqu'il y a range(height) rangés.
            to_take = (self.width * 3 + 3) & -4
            padding = 0 if to_take % 8 == 0 else (to_take % 8) // 2
            height, data = self.height, self.data
            scanlines = [b''.join([b'0', data[to_take*y:to_take*y+to_take-padding]]) for y in range(height)]

            # les "magic bytes", le marqueur du début de fichier qui indique
            # que c'est un fichier png
            magic = pack(b'>8B', 137, 80, 78, 71, 13, 10, 26, 10)

            # Les metadata du fichiers : taille de l'image, une somme de
            # controle, la profondeur de couleur, méthode de compression,
            # le mode d'entrelacement, etc.
            # Header: size, marker, data, CRC32
            ihdr = [b'', b'IHDR', b'', b'']
            ihdr[2] = pack(b'>2I5B', self.width, self.height, 8, 2, 0, 0, 0)
            ihdr[3] = pack(b'>I', zlib.crc32(b''.join(ihdr[1:3])) & 0xffffffff)
            ihdr[0] = pack(b'>I', len(ihdr[2]))

            # l'image en elle même avec un marker de départ, les pixels
            # et une somme de controle
            # Data: size, marker, data, CRC32
            idat = [b'', b'IDAT', b'', b'']
            idat[2] = zlib.compress(b''.join(scanlines), 9)
            idat[3] = pack(b'>I', zlib.crc32(b''.join(idat[1:3])) & 0xffffffff)
            idat[0] = pack(b'>I', len(idat[2]))

            # Les metadata à la fin du fichier qui sont vides.
            # Footer: size, marker, None, CRC32
            iend = [b'', b'IEND', b'', b'']
            iend[3] = pack(b'>I', zlib.crc32(iend[1]) & 0xffffffff)
            iend[0] = pack(b'>I', len(iend[2]))

            # On écrit le fichier, et on retourne son nom
            fileh.write(magic + b''.join(ihdr) + b''.join(idat) + b''.join(iend))
            return output
        return None

# Un exemple d'usage avec l'habituel if __name__ pour éviter de
# le lancer à l'import
if __name__ == '__main__':

    systems = {
        'Darwin' : MSSMac,
        'Linux'  : MSSLinux,
        'Windows': MSSWindows
    }
    try:
        MSS = systems[system()]
    except KeyError:
        err = 'System "{0}" not implemented.'.format(system())
        raise NotImplementedError(err)

    try:
        mss = MSS(debug=False)

        # One screen shot per monitor
        for filename in mss.save():
            print('File "{0}" created.'.format(filename))

        # A shot to grab them all :)
        for filename in mss.save(oneshot=True):
            print('File "{0}" created.'.format(filename))
    except Exception as ex:
        print(ex)
        raise

]]>
http://sametmax.com/explication-de-code-python-mss/feed/ 9 9219
Ouvrir un fichier avec le bon programme en Python http://sametmax.com/ouvrir-un-fichier-avec-le-bon-programme-en-python/ http://sametmax.com/ouvrir-un-fichier-avec-le-bon-programme-en-python/#comments Thu, 17 Oct 2013 10:09:59 +0000 http://sametmax.com/?p=7469 Votre logiciel doit permettre d’ouvrir un fichier avec un programme externe. Oui mais lequel ?

Les OS ont des réglages par défaut pour chaque type de fichier, et on peut demander “ouvrir le prog pour ce type de fichier par défaut”. Par exemple, moi, si je demande d’ouvrir un fichier vidéo, je m’attend à ce que VLC soit lancé.

Voilà comment faire ça en Python :

import subprocess
import sys
import os

def run_file(path):

    # Pas de EAFP cette fois puisqu'on est dans un process externe,
    # on ne peut pas gérer l'exception aussi facilement, donc on fait
    # des checks essentiels avant.

    # Vérifier que le fichier existe
    if not os.path.exists(path):
        raise IOError('No such file: %s' % path)

    # On a accès en lecture ?
    if hasattr(os, 'access') and not os.access(path, os.R_OK):
        raise IOError('Cannot access file: %s' % path)

    # Lancer le bon programme pour le bon OS :

    if hasattr(os, 'startfile'): # Windows
        # Startfile est très limité sous Windows, on ne pourra pas savoir
        # si il y a eu une erreu
        proc = os.startfile(path)

    elif sys.platform.startswith('linux'): # Linux:
        proc = subprocess.Popen(['xdg-open', path], 
                                 # on capture stdin et out pour rendre le 
                                 # tout non bloquant
                                 stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    elif sys.platform == 'darwin': # Mac:
        proc = subprocess.Popen(['open', '--', path], 
                                stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    else:
        raise NotImplementedError(
            "Your `%s` isn't a supported operatin system`." % sys.platform)

    # Proc sera toujours None sous Windows. Sous les autres OS, il permet de
    # récupérer le status code du programme, and lire / ecrire sur stdin et out
    return proc

C’était le petit snippet sympas du jour !

P.S : si quelqu’un utilise BDSM BSD ou Solaris, je veux bien qu’il complète le snippet.

]]>
http://sametmax.com/ouvrir-un-fichier-avec-le-bon-programme-en-python/feed/ 18 7469
Gagner de la place sur Windows 7 http://sametmax.com/gagner-de-la-place-sur-windows-7/ http://sametmax.com/gagner-de-la-place-sur-windows-7/#comments Tue, 05 Mar 2013 10:02:24 +0000 http://sametmax.com/?p=5236 Vous avez un dual boot que vous utilisez rarement, mais disons totalement hypothétiquement que vos amis se mettent à jouer à League of Legend et que soudainement vous allez la réutiliser.

Vous bootez, et la stupeur vous gagne, il reste 2,1Mo de place libre sur la partition. Après avoir appuyé 3 fois sur “vider la corbeille” pour être bien sûr, le desespoir vous gagne.

Don’t panic.

Détecter et virer les gros fichiers

Vous téléchargez Win Dir Stats, vous lancez un scan complet, et il va vous montrer les plus gros fichiers / dossiers de votre ordi. Il ne vous reste plus qu’à surpprimer manuellement tous ces parasites avec shift + delete pour les envoyer directement dans l’oblivion, sans passer par la case corbeille, et sans toucher 20 000 francs.

Desinstaller massivement

L’utilitaire de désinstallation de Windows est à chier. Absolute uninstaller est rapide et vous permet de supprimer allégrement tous ces boulets qui vous encombre.

Vider winsxs

C’est un dossier que Windows 7 ADOOOOOOOOORE charger de plein de merdes qui ne sont en fait que des fichiers temporaires. Ca peut monter jusqu’à 20 Go. Si.

Donc on cherche cmd.exe dans le menu, on clic droit > “Executer en tant qu’Administrateur” et on lance la commande :

DISM /online /Cleanup-Image /SpSuperseded

C’est un peu long, mais ça libère quelques Go.

Se débarrasser du fichier hyberfil.sys

Quand on est en dual boot avec Linux comme système principal, on en a rien à branlé de la mise en veille de Windows. Qui prend quand même 4Go avec sont super fichier hyberfil.sys.

Donc pareil, vous lancez une console en tant qu’administrateur, et vous lui fermez sa gueule:

powercfg.exe -h off

Un petit reboot, et l’hybernation est désactivée.

]]>
http://sametmax.com/gagner-de-la-place-sur-windows-7/feed/ 31 5236
Programmer confortablement en Python sous Windows http://sametmax.com/programmer-confortablement-en-python-sous-windows/ http://sametmax.com/programmer-confortablement-en-python-sous-windows/#comments Tue, 25 Dec 2012 19:05:46 +0000 http://sametmax.com/?p=3865 notepad++ fait très bien l'affaire) et la ligne de commande. Problème, la ligne de commande est à chier sous Windows. Alors oui, vous pouvez coder dans ces conditions, mais franchement, pourquoi ne pas passer 30 minutes pour vous mettre à l'aise ?]]> Python est un langage qui demande très peu pour programmer: pas d’IDE, de compilateur, de RAD ou autre. Juste un petit éditeur de texte (notepad++ fait très bien l’affaire) et la ligne de commande.

Problème, la ligne de commande est à chier sous Windows. Alors oui, vous pouvez coder dans ces conditions, mais franchement, pourquoi ne pas passer 30 minutes pour vous mettre à l’aise ?

Max est sous Mac, et je suis sous Ubuntu, mais c’est pas pour ça qu’on oublie nos racines. On a tous les deux commencé sous Windows. Perso, j’ai fait mes premiers clics de souris sur 3.1 (avant j’avais pas de souris :-)). Alors suivez le guide.

Avoir les commandes Python sous la main

Les commandes installées par des scripts Python, parfois même la commande python elle-même, ne sont pas toujours accessibles depuis la console. Assurez-vous donc d’avoir bien “c:\Python27;c:\Python27\Scripts;” à la fin de votre System PATH.

Améliorer la console

Une fois que vous avez installé Python (toujours v2.7 en 32 bits. TOUJOURS), vous avez accès au shell. Premier constat, copier / coller dans un terminal cmd.exe est aussi plaisant qu’une coloscopie un lendemain de cuite (pour vous et le praticien).

Idéalement, il faut installer un terminal alternatif. Le logiciel opensource Console2 est parfait pour ça: on peut faire du copier / coller facilement, il a des onglets, on peut régler la taille de la fenêtre…

Pour l’installer suffit de télécharger le zip, l’extraire dans “C:\Programmes Files” et faire un raccourci sur le bureau vers “C:\Program Files\Console2\Console.exe”.

Améliorer le shell Python

Le shell Python est fantastique, à l’exception de tous les autres. Donc votre premier réflexe, c’est bien entendu d’installer iPython. Sauf que Windows n’a pas readline, du coup vous n’allez pas avoir la coloration du prompt ni la completion du code. Ca craint.

C’est là que pip va encore une fois nous sauver la mise:

pip install pyreadline

Et votre shell iPython, lancé depuis Console2 est maintenant un outil presque aussi confortable à utiliser que sous Linux ou Mac avec historique, auto indent et tout le toutim. Presque, faut pas abuser non plus.

Et pour quelques outils de plus

Windows n’ayant pas de truc comme grep, installer grin va vous permettre de rapidement et confortablement chercher dans votre code. Donc, pip install grin.

Mais aller directement dans le dossier voulu depuis la ligne de commande à grand coup de cd, c’est relou. On va arranger ça.

Ouvre l’éditeur de registre (“Windows + R” puis regedit puis “enter”) et naviguez dans l’arboresence jusqu’à:

HKEY_CLASSES_ROOT\Directory\shell\

Cliquez droit dans le panel de droite, et faites “Nouveau > Clé” et appelez la clé “open_shell”. Puis cliquez sur le dossier ainsi créé, et dans le panel de droite, cliquez sur l’espèce d’icône “AB” intitulée “(Par défaut)”, et donnez lui la valeur “Ouvrir Console2 ici”.

Dans le dossier “open_shell”, créez un dossier “Command” (en faisant “Clic Droit > Nouveau > Clé”).

Donnez à “(Par défaut)” pour valeur le chemin vers Console2 suivit de ‘-d “%1″‘, par exemple pour moi:

"C:\Program Files (x86)\Console2\Console.exe" -d "%1"

Fermer l’explorateur de fichiers, et réouvrez le. Hop ! Vous avez une nouvelle entrée dans le menu du clic droit qui permet d’ouvrir Console2 dans n’importe quel dossier.

]]>
http://sametmax.com/programmer-confortablement-en-python-sous-windows/feed/ 27 3865
Ajouter un chemin à la variable d’environnement PATH sous Windows http://sametmax.com/ajouter-un-chemin-a-la-variable-denvironnement-path-sous-windows/ http://sametmax.com/ajouter-un-chemin-a-la-variable-denvironnement-path-sous-windows/#comments Thu, 18 Oct 2012 20:19:16 +0000 http://sametmax.com/?p=2642 Il y a beaucoup de tutos qui demandent de simplement “ajouter le résultat au PATH”, mais assez peu expliquent pourquoi et comment le faire.

Comme on aime bien faire les choses à l’envers chez Sam et Max, on va d’abord expliquer comment, et ensuite pourquoi.

Modifier le PATH sous Windows 7 et XP

Il faut d’abord ouvrir l’outil d’édition des variables d’environnement.

Sous Windows XP, c’est:

  • Clic droit sur l’icône “Ordinateur” de votre Bureau (ou dans le panel de gauche de l’explorateur de fichiers) et dans le menu-contextuel, allez sur “Propriétés”.
  • Dans la fenêtre “Propriétés système” qui vient de s’ouvrir, allez dans l’onglet “Avancé”
  • Enfin, cliquez sur le bouton “Variables d’environnement…”

Sous Windows 7, c’est presque pareil:

  • Clic droit sur l’icône “Ordinateur” de votre Bureau (ou dans le panel de gauche de l’explorateur de fichiers) et dans le menu-contextuel, allez sur “Propriétés”.
  • Dans la grande fenêtre qui s’ouvre, cliquez sur le lien “Paramètres système avancés” dans la liste de gauche.
  • Dans la nouvelle fenêtre ainsi ouverte, cliquez sur le bouton “Variables d’environnement…”

Dans les deux cas, vous allez vous retrouver avec la fenêtre le plus détestée du monde de Windows, la fameuse fenêtre “Variables d’envionnement”. Toute petite, illisible, est pas redimensionable. Vous ne l’oublierez plus jamais:

Capture d'écran de la fenêtre des variables d'environnement de Windows

Oh mon dieu, il nous fonce dessus !

Dans cette fenêtre, dans la partie “Variables systèmes” (la liste du bas), il faut cliquer sur la ligne qui contient ‘Path’, de telle sorte qu’elle soit sélectionnée (en couleur alors que les autres items de la listes ne le sont pas). Il vous faudra sûrement scroller un peu pour la trouver.

Ensuite il faut cliquer sur le bouton “Modifier…”.

Là s’ouvre une fenêtre avec deux champs, le deuxième, “Valeur de la variable”, est celui qui nous intéresse. Il est absolument chiant à modifier, alors faites plutôt un copier/coller dans un éditeur de texte à part: Ctrl + A pour tout selectioner, Ctrl + C pour tout copier, puis Ctrl + V pour tout coller ailleurs, car je ne suis pas sûr que le clic droit fonctionne dans cette grosse bouse.

Vous devriez avoir un texte qui ressemble à ceci (pas exactement le même, mais proche):

%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\;C:\Program Files\Intel\WiFi\bin\;C:\Program Files\Common Files\Intel\WirelessCommon\;

Ne retirez rien de cette valeur si vous ne savez pas ce que vous faites.

Pour ajouter quelque chose au PATH de Windows, il suffit de le mettre tout collé à la fin, suivit d’un ‘;’

Par exemple, pour rajouter le chemin vers l’installation de Python dans le PATH de Windows:

%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\;C:\Program Files\Intel\WiFi\bin\;C:\Program Files\Common Files\Intel\WirelessCommon\;C:\Python27;

Attention, si vous avez copié/collé le contenu ailleurs, supprimez bien l’ancien contenu en le remplaçant complètement par le nouveau (Ctrl + A, Ctrl + V) sinon vous allez tout péter.

A quoi servent les variables d’environnement et le PATH ?

Sous tous les OS, il y a des variables qui déterminent le fonctionnement de celui-ci (ou plutôt du shell, mais ce n’est pas important). Elles permettent à l’utilisateur, en changeant la variable, de choisir certains comportement primitifs du système.

Par exemple, quand vous tapez une commande dans un terminal, le système va utiliser la variable d’environnement PATH. Il va prendre chaque dossier listé dans PATH, et regarder si il trouve la commande dedans. Si il trouve cette commande, il la lance, et arrête la recherche. Si il ne trouve pas la commande, il va vous dire que la commande n’existe pas.

Ainsi, si vous avez installé Python mais que le shell Python ne se lance pas quand vous entrez la commande python dans un terminal, ajouter C:\Python27; au PATH peut résoudre le problème: Windows va maintenant chercher dans ce dossier également pour voir si la commande existe.

]]>
http://sametmax.com/ajouter-un-chemin-a-la-variable-denvironnement-path-sous-windows/feed/ 11 2642