Архив за месяц: Июнь 2020

Прохождение квеста по запуску OpenGL для Python3

Оказалось, что если просто взять и открыть первый попавшийся урок по OpenGL на Python, то скорее всего ничего не получится. Всё пробовал на Python 3.6.5 под Windows x64. Для начала. пробуем установать пакеты, которые используются в скриптах:

\Python36\python.exe -m pip install PyOpenGL
Collecting PyOpenGL
  Using cached PyOpenGL-3.1.5-py3-none-any.whl (2.4 MB)
Installing collected packages: PyOpenGL
Successfully installed PyOpenGL-3.1.5
\Python36\python.exe -m pip install PyOpenGL-accelerate
Collecting PyOpenGL-accelerate
  Using cached PyOpenGL_accelerate-3.1.5-cp36-cp36m-win_amd64.whl (329 kB)
Installing collected packages: PyOpenGL-accelerate
Successfully installed PyOpenGL-accelerate-3.1.5

Теперь попробуем выполнить скрипт из примеров примерно с таким содержимым:

from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
...
glutInit()
...

Результат получится примерно такой:

\Python36\python.exe testopengl.py
Traceback (most recent call last):
  File "testopengl.py", line 30, in <module>
    glutInit()
  File "Python\Python36\lib\site-pac
kages\OpenGL\GLUT\special.py", line 333, in glutInit
    _base_glutInit( ctypes.byref(count), holder )
  File "Python\Python36\lib\site-pac
kages\OpenGL\platform\baseplatform.py", line 425, in __call__
    self.__name__, self.__name__,
OpenGL.error.NullFunctionError: Attempt to call an undefined function glutInit,
check for bool(glutInit) before calling

По советам из интернета скачиваем freeglut (качал отсюда https://www.transmissionzero.co.uk/files/software/development/GLUT/freeglut-MSVC-3.0.0-2.mp.zip), извлекаем оттуда и ложим рядом со скриптом файл freeglut.dll. Пробуем запустить скрипт снова — получаем ту же самую ошибку. В интернете пишут, что в стандартных пакетах питона какой-то касяк, поэтому их нужно удалить:

\Python36\python.exe -m pip uninstall pyopengl
Found existing installation: PyOpenGL 3.1.5
Uninstalling PyOpenGL-3.1.5:
  Would remove:
    python\python36\lib\site-package
s\opengl\*
    python\python36\lib\site-package
s\pyopengl-3.1.5.dist-info\*
Proceed (y/n)? y
  Successfully uninstalled PyOpenGL-3.1.5

\Python36\python.exe -m pip uninstall pyopengl-accelerate
Found existing installation: PyOpenGL-accelerate 3.1.5
Uninstalling PyOpenGL-accelerate-3.1.5:
  Would remove:
    python\python36\lib\site-package
s\opengl_accelerate\*
    python\python36\lib\site-package
s\pyopengl_accelerate-3.1.5.dist-info\*
Proceed (y/n)? y
  Successfully uninstalled PyOpenGL-accelerate-3.1.5

А теперь, говорят, надо скачивать альтернативные пакеты вот отсюда: https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyopengl. Я схватился за самый свежий пакет PyOpenGL‑3.1.5‑cp39‑cp39‑win_amd64.whl, и он мне не подошёл:

\Python36\python.exe -m pip install PyOpenGL-3.1.5-cp39-cp39-win_amd64.whl
ERROR: PyOpenGL-3.1.5-cp39-cp39-win_amd64.whl is not a supported wheel on this p
latform.

Как я понял, в названии пакета 39 — это версия питона, для которого предназначен пакет, то есть мне для 3.6.5 нужны пакеты с цифрой 36:

\Python36\python.exe -m pip install PyOpenGL-3.1.5-cp36-cp36m-win_amd64.whl
Processing pyopengl-3.1.5-cp36-cp36m-win_amd64.whl
Installing collected packages: PyOpenGL
Successfully installed PyOpenGL-3.1.5

\Python36\python.exe -m pip install PyOpenGL_accelerate-3.1.5-cp36-cp36m-win_am
d64.whl
Processing pyopengl_accelerate-3.1.5-cp36-cp36m-win_amd64.whl
Installing collected packages: PyOpenGL-accelerate
Successfully installed PyOpenGL-accelerate-3.1.5

Ну и теперь пробуем запустить скрипт:

python.exe testopengl.py
Python OpenGL
Python OpenGL

Короче победа. Все загрузки (пакеты для питона и freeglut) проверялись антивирусом, но если будете повторять — качайте на свой страх и риск.

auto-py-to-exe — компилятор питоновских скриптов для windows

Проверено на Python 3 — работает

Установка:

>c:\users\user\AppData\Local\Programs\Python\Python36\python.exe -m pip install auto-py-to-exe
Collecting auto-py-to-exe
  Downloading auto_py_to_exe-2.7.4-py2.py3-none-any.whl (74 kB)
     |████████████████████████████████| 74 kB 84 kB/s
....
Successfully installed Eel-0.11.0 altgraph-0.17 auto-py-to-exe-2.7.4 bottle-0.12
.18 bottle-websocket-0.2.9 cffi-1.14.0 future-0.18.2 gevent-20.6.0 gevent-websoc
ket-0.10.1 greenlet-0.4.16 pefile-2019.4.18 pycparser-2.20 pyinstaller-3.6 pywin
32-ctypes-0.2.0 whichcraft-0.6.1 zope.event-4.4 zope.interface-5.1.0

После успешной установки в папке Python36\Scripts\ появится auto-py-to-exe.exe, запускаем его

auto-py-to-exe
auto-py-to-exe

Выбираем скрипт, выставляем нужные настройки и жмём снизу «CONVERT .PY TO .EXE«. Появляется папка output с откомпилированным скриптом program.exe

Python скрипт для сбора информации по файлам в папке

# -*- coding: utf-8 -*-

'''
на входе папка или файл, на выходе CSV: имя файла;сумма мд5;папка полностью;имя компа
'''

import os
import hashlib
import platform
import time
import sys

class MyItem:
  def __init__(self, f):
    self.hostname = hostname
    self.md5sum = md5(f)
    self.size = os.path.getsize(f)
    self.created = "%s" % time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(os.path.getctime(f)))
    self.modified = "%s" % time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(os.path.getmtime(f)))
    self.ext = os.path.splitext(f)[1]
    self.full_dir, self.file_name = os.path.split(os.path.abspath(f))

def md5(fname):  # https://stackoverflow.com/questions/3431825/generating-an-md5-checksum-of-a-file
    hash_md5 = hashlib.md5()
    with open(fname, "rb") as f:
        for chunk in iter(lambda: f.read(4096), b""):
            hash_md5.update(chunk)
    return hash_md5.hexdigest()

hostname = platform.node()

result_header = "file_name;md5sum;size;created;modified;ext;full_path;hostname\n"
result_file = open('./result.csv','w')
result_file.write(result_header)

targets = []

if len(sys.argv) > 1:
  targets = sys.argv[1::]
else:
  targets.append(".")

for t in targets:
  if os.path.exists(t):
    if os.path.isdir(t):
      for current_dir, dirs, files in os.walk(t):
        for f in files:
          try:
            i = os.path.join(current_dir, f)
            print(i)
            item = MyItem(i)
            result_file.write('%s;%s;%s;%s;%s;%s;%s;%s\n'%(item.file_name, item.md5sum, item.size, item.created, item.modified, item.ext, item.full_dir, item.hostname))
          except:
            print('Error!')
    if os.path.isfile(t):
      try:
        i = t
        print(i)
        item = MyItem(i)
        result_file.write('%s;%s;%s;%s;%s;%s;%s;%s\n'%(item.file_name, item.md5sum, item.size, item.created, item.modified, item.ext, item.full_dir, item.hostname))
      except:
        print('Error!')

result_file.close()

Консольный калькулятор для Windows на Python

Консольный калькулятор на Python

Управление: стрелки на клавиатуре и Enter, выход из программы — Esc

Исходник calc.py:

# -*- coding: utf-8 -*-
# kmsvsr.ru 2020-06-06

import msvcrt
from decimal import *

def makeDisplayString(s):
  result = " " * (20 - len(s)) + s
  return result

class MyCalculator:
  def __init__(self):
    self.last_result = Decimal('99999999999999999999.0')
    self.result = Decimal('0.0')
    self.display_default='''
=====Калькулятор==========
|[ 00000000000000000000 ]|
|========================|
|                        |
|[ 1 ] [ 2 ] [ 3 ]  [ + ]|
|                        |
|[ 4 ] [ 5 ] [ 6 ]  [ - ]|
|                        |
|[ 7 ] [ 8 ] [ 9 ]  [ x ]|
|                        |
|[ 0 ] [ . ] [ = ]  [ / ]|
|                        |
==========================
'''
    self.display = self.display_default
    self.keys=("123+", "456-", "789x", "0.=/")

    self.ACTION_EMPTY = 0
    self.ACTION_PLUS = 1
    self.ACTION_MINUS = 2
    self.ACTION_MULTIPLY = 3
    self.ACTION_DIVIDE = 4

    self.last_action = self.ACTION_EMPTY
    
    self.visible = 1
    
    self.display_digits = "0"  # цифры, которые видно на калькуляторе, не больше 20
    self.last_display_digits = ""
    self.updateDigitsOnDisplay()
    
    # положение курсора слева сверху
    self.curx=0
    self.cury=0
    
    self.needNewDigit = 0  # 1 после кнопки действия
    
  def updateKeysOnDisplay(self):
    self.display = self.display.replace('[ %s ]' % self.keys[self.cury][self.curx], '[>%s<]' % self.keys[self.cury][self.curx])

  def updateDigitsOnDisplay(self):
    if len(self.display_digits) > 0:
      self.display = self.display.replace('00000000000000000000', makeDisplayString(self.display_digits) )
    else:
      self.display = self.display.replace('00000000000000000000', makeDisplayString(self.last_display_digits) )
    
  def resetDisplay(self):
    self.display = self.display_default
    
  def show(self):
    self.visible = 1
    self.redraw()
    
  def redraw(self):
    self.resetDisplay()
    self.updateDigitsOnDisplay()
    self.updateKeysOnDisplay()
    if self.visible == 1:
      print(self.display)
      
  def calc(self):
    try:
      new_value = Decimal('%s' % self.display_digits)
    except:
      new_value = Decimal('99999999999999999999.0')
      
    if self.last_action == self.ACTION_PLUS:
      self.result = self.last_result + new_value
    if self.last_action == self.ACTION_MINUS:
      self.result = self.last_result - new_value
    if self.last_action == self.ACTION_MULTIPLY:
      self.result = self.last_result * new_value
    if self.last_action == self.ACTION_DIVIDE:
      self.result = self.last_result / new_value
    if self.last_action == self.ACTION_EMPTY:
      self.result = new_value
    if self.last_action != self.ACTION_EMPTY:
      self.display_digits = "%s" % self.result
    self.last_result = self.result
    self.needNewDigit = 1
    self.last_display_digits = self.display_digits
    
'''
 H
KPM
'''

calc = MyCalculator()

calc.show()

while True:
  if calc.needNewDigit == 1:
    calc.display_digits = ""
    calc.needNewDigit = 0
  lastkey = msvcrt.getch()
  #print(lastkey)
  if lastkey == b'\x1b':  # Esc
    break
  if lastkey == b'K':  # left
    if calc.curx > 0:
      calc.curx -= 1
    calc.updateKeysOnDisplay()
  if lastkey == b'P':  # down
    if calc.cury < 3:
      calc.cury += 1
    calc.updateKeysOnDisplay()
  if lastkey == b'M':  # right
    if calc.curx < 3:
      calc.curx += 1
    calc.updateKeysOnDisplay()
  if lastkey == b'H':  # up
    if calc.cury > 0:
      calc.cury -= 1
    calc.updateKeysOnDisplay()
  if lastkey == b'\r':
    #Разбираемся, какая кнопка нажата на калькуляторе
    selected = calc.keys[calc.cury][calc.curx]
    if calc.curx < 3 and calc.cury < 3:  # цифры больше 0
      newd = selected  # новая нажатая цифра
      if calc.display_digits == "0":
        calc.display_digits = newd
      elif len(calc.display_digits) < 20:
        calc.display_digits += newd
    if calc.curx == 0 and calc.cury == 3:  # 0
      if calc.display_digits != "0" and len(calc.display_digits) < 20:
        calc.display_digits += selected
    if calc.curx == 1 and calc.cury == 3:  # . (точка)
      if len(calc.display_digits) < 20 and '.' not in calc.display_digits:  
        calc.display_digits += selected
    #.needNewDigit  
    #.last_action
    if calc.curx == 3 and calc.cury == 0:  # +
      calc.calc()
      calc.last_action = calc.ACTION_PLUS
    if calc.curx == 3 and calc.cury == 1:  # -
      calc.calc()
      calc.last_action = calc.ACTION_MINUS
    if calc.curx == 3 and calc.cury == 2:  # x
      calc.calc()
      calc.last_action = calc.ACTION_MULTIPLY
    if calc.curx == 3 and calc.cury == 3:  # /
      calc.calc()
      calc.last_action = calc.ACTION_DIVIDE
    if calc.curx == 2 and calc.cury == 3:  # =
      calc.calc()
      calc.last_action = calc.ACTION_EMPTY

  print('\n'*25)
  calc.redraw()

Скачать: Скачать

Python: чтение нажатия кнопок в консоли Windows

import msvcrt
msvcrt.getch()
b’f’
msvcrt.getch()
b’\xe0′
msvcrt.getch()
b’M’
msvcrt.getch()
b’\xe0′
msvcrt.getch()
b’H’
msvcrt.getch()
b’\r’
for i in range(0,5):
… msvcrt.getch()

b’1′
b’f’
b’5′
b’\r’
b’\xe0′

Шпаргалка по типам данных в Python

Text Type: str
Numeric Types: int, float, complex
Sequence Types: list(список), tuple(кортеж), range
Mapping Type: dict(словарь)
Set Types: set, frozenset
Boolean Type: bool
Binary Types: bytes, bytearray, memoryview
x = "Hello World" str
x = 20 int
x = 20.5 float
x = 1j complex
x = ["apple", "banana", "cherry"] list(список)
x = ("apple", "banana", "cherry") tuple(кортеж)
x = range(6) range
x = {"name" : "John", "age" : 36} dict(словарь)
x = {"apple", "banana", "cherry"} set
x = frozenset({"apple", "banana", "cherry"}) frozenset
x = True bool
x = b"Hello" bytes
x = bytearray(5) bytearray
x = memoryview(bytes(5)) memoryview

Python: как узнать размер окна терминала консоли в Windows и Linux

Windows

Найдено здесь: https://code.activestate.com/recipes/440694-determine-size-of-console-window-on-windows/

from ctypes import windll, create_string_buffer

# stdin handle is -10
# stdout handle is -11
# stderr handle is -12

h = windll.kernel32.GetStdHandle(-12)
csbi = create_string_buffer(22)
res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)

if res:
    import struct
    (bufx, bufy, curx, cury, wattr,
     left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)
    sizex = right - left + 1
    sizey = bottom - top + 1
else:
    sizex, sizey = 80, 25 # can't determine actual size - return default values

print sizex, sizey

Проверил, работает

Linux

>>> import os
>>> os.get_terminal_size()
os.terminal_size(columns=80, lines=24)
>>> os.get_terminal_size().columns
80
>>> os.get_terminal_size().lines
24
>>>