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.
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
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.
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"
.
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”.
]]>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 :
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
]]>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.
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.
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.
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.
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.
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.
]]>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.
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.
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”.
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.
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.
]]>Comme on aime bien faire les choses à l’envers chez Sam et Max, on va d’abord expliquer comment, et ensuite pourquoi.
Il faut d’abord ouvrir l’outil d’édition des 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:
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.
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.