Страница 1 из 3

Python: Tkinter GUI

Добавлено: 04 янв 2019, 14:56
Olej
Возникла такая интересная задача... :
- сформированные программами распознавания лиц на Python (см. распознавание лиц) выводить в окна-фреймы-виджеты GUI приложений...
- это должно быть на Python 3 (не Python 2).

P.S. Я по этому поводу даже специальное руководство написал для заказчика ... поскольку это не оговаривалось и не запрещалось - приложу его сюда, может кому-то пригодится.

Python: Tkinter GUI

Добавлено: 04 янв 2019, 15:07
Olej
Olej писал(а): - сформированные программами распознавания лиц на Python (см. распознавание лиц) выводить в окна-фреймы-виджеты GUI приложений...
Вопрос в том, как внутренние форматы хранения изображений пакетов а). numpy (он же - компьютерное зрение OpenCV) и б). Pillow (он же Dlib и OpenFace) подсунуть на вывод графической системе Tcl/Tk через Python3 модуль tkinter? ... а заодно и взаимные преобразования форматов между а). и б).

Преобразование форматов (с их отображением) - pil2cv.py:

Код: Выделить всё

#!/usr/bin/python3 
# -*- coding: utf-8 -*- 
import sys
import numpy
from PIL import Image
import cv2
import dlib
import subprocess

# Take the image file name from the command line
with Image.open( sys.argv[ 1 ] ) as pillow_image:
    pillow_image.show()
    cv_image = numpy.array( pillow_image )
    cv_image = cv2.resize( cv_image, ( 0, 0 ), fx = 0.6, fy = 0.6 )
    print( type( cv_image ), cv_image.ndim, cv_image.shape, cv_image.dtype )
    print( '[0][0]=>{} , [0][1]=>{}'.
           format( cv_image[ 0 ][ 0 ], cv_image[ 0 ][ 1 ] ) )
    if cv_image.ndim > 2:  
        pillow_image = Image.fromarray( cv_image, "RGB" )
    else:
        pillow_image = Image.fromarray( cv_image, "L" )
    pillow_image.show()
    print( type( pillow_image ), pillow_image.size, pillow_image.mode )
    print( '(0,0)=>{} , (1,0)=>{}'.
           format( pillow_image.getpixel( ( 0, 0 ) ), pillow_image.getpixel( ( 1, 0 ) ) ) )
dlib.hit_enter_to_continue()
subprocess.call( [ 'killall', 'display' ] )
С этим кодом небольшая загвоздка состоит в том, что сам вывод изображений на экран Pillow (по крайней мере в Debian Linux ... да и в не Debian, похоже, тоже) делает через графические средства проекта ImagMagic, который нужно предварительно установить ... что особенно важно на одноплатных ARM SBC, типа Dragon Board 410c, Rapsbery Pi, Orange Pi ...

Re: Python - графика

Добавлено: 04 янв 2019, 15:16
Olej
Olej писал(а): Преобразование форматов (с их отображением) - pil2cv.py:
Вывод самых разнообразных внешних файлов изображений в фрейм Tcl/Tk - fil2tk.py:

Код: Выделить всё

#!/usr/bin/python3
# -*- coding: utf-8 -*- 
import sys
import numpy as np
import tkinter as tk 
from tkinter import filedialog

root = tk.Tk()
root.geometry( '600x400' )
#root.geometry('600x400+200+100')
#root.resizable( width = False, height = False )
root.title( 'Изображение' )
b1 = tk.Button( root )
b2 = tk.Button( root, text = 'Открыть' )

def showImage():
    options = {                                          # define options for opening
        'title'     : "Choose an image file",
        'filetypes' : [ ( "Image", ("*.jpg","*.png","*.gif" ) ), 
                        ( "Other", ("*.ppm","*.pgm","*.bmp" ) ), 
                        ( "All files", "*.*" ) ] 
              }
    file_name = filedialog.askopenfilename( **options )  # select image file
    global b1
    b1.destroy()
    print( 'image file: {}'.format( file_name ) )
    print( root.geometry() )
    try:                                                 # load image as is ( .jpg, .png, .gif, ... )
        tkimg = tk.PhotoImage( file = file_name )
    except Exception as err:
        print( 'error: {}'.format( err ) )
        return
    print( type( tkimg ) )
    print( tkimg.__str__() )
    print( 'height={} width={}'.format( tkimg.height(), tkimg.width() ) )
    b1 = tk.Button( root, image = tkimg )
    b1.image = tkimg      # store a reference to the image as an attribute of the widget
    b1.pack( side = 'top' )
    return    

b2[ 'command' ] = showImage                              #b.bind( '<Button-1>', showImage )
b2.pack( side='bottom' )
root.mainloop()
Выглядит это так, как показано на картинках ниже.
Так же точно выглядит выполнение последующих экзамплов.

Re: Python - графика

Добавлено: 04 янв 2019, 15:26
Olej
Olej писал(а): Вывод самых разнообразных внешних файлов изображений в фрейм Tcl/Tk - fil2tk.py:
Вывод внутренних представлений изображений масссивами numpy (и OpenCV)...
В документации (по tkinter, которая очень скудная) утверждается, что вызов PhotoImage() с ключевым параметром data=… (вместо file=…) создаёт образ изображения для отображения, используя данные уже размещённые в памяти.
Но! Как оказалось, PhotoImage() готов принять, в виде данных, в этом случае не поток сырых байт, а только полный формат представления одного из предусмотренных форматов файлов (с заголовками, «магическими байтами», сжатием для сжимаемых форматов и т. д.).
Для решения этой проблемы (а также для любых пакетов Python и их форматов) нужно сформировать поток байт в памяти, имитирующий простейшие форматы изображений. И здесь нам на помощь приходят так называемые простые форматы изображения PNM (Netpbm format: .pbm, .pgm, .ppm). Мы должны сформировать соответствующий заголовок формата, а далее следующий за ним поток байт собственно изображения (см. Portable anymap).
Приложение cv2tk.py :

Код: Выделить всё

#!/usr/bin/python3 
# -*- coding: utf-8 -*- 
import numpy as np
import cv2
import tkinter as tk 
from tkinter import filedialog

root = tk.Tk()                                           # main frame
root.geometry( '600x400' )
#root.geometry('600x400+200+100')
#root.resizable( width = False, height = False )
root.title( 'Изображение' )
b1 = tk.Button( root )                                   # image frame
b2 = tk.Button( root, text = 'Открыть' )

def showImage():
    options = {                                          # define options for opening
        'title'     : "Choose an image file",
        'filetypes' : [ ( "Image", ("*.jpg","*.png","*.gif" ) ), 
                        ( "Other", ("*.ppm","*.pgm","*.bmp" ) ), 
                        ( "All files", "*.*" ) ]
              }
    file_name = filedialog.askopenfilename( **options )  # select image file
    global b1
    b1.destroy()
    print( 'image file: {}'.format( file_name ) )
#    print( root.geometry() )
    try:                                                 # load image as is ( .jpg, .png, .gif, ... )
        image = cv2.imread( file_name, cv2.IMREAD_LOAD_GDAL )
        if not isinstance( image, np.ndarray ):   # to see if reading error occurs
            raise Exception ( 'Error format image file', type( image ) )
    except Exception as err:
        print( 'error: {}'.format( err ) )
        return
    print( type( image ), image.ndim, image.shape, image.dtype )  
    print( '[0][0]=>{}'.format( image[ 0 ][ 0 ] ) )    
    ( h, w ) = ( image.shape[ 0 ], image.shape[ 1 ] ) 
    if image.ndim > 2:                                   # if RGB scale
        pnm_type = b'P6 '
    else:                                                # if gray scale
        pnm_type = b'P5 '
    head = pnm_type + bytes( str( w ) + ' ' + str( h ) + ' 255 ', 'UTF8' )   
#    print( head ) 
    xdata = head + image.tobytes()
    tkimg = tk.PhotoImage( width = w, height = h, data = xdata, format = 'PPM' )    
    print( 'height={} width={}'.format( tkimg.height(), tkimg.width() ) )
    b1 = tk.Button( root, image = tkimg )
    b1.image = tkimg                                     # store a reference to the image as an attribute of the widget
    b1.pack( side = 'top' )
    return    

b2[ 'command' ] = showImage                              # b.bind( '<Button-1>', showImage )
b2.pack( side='bottom' )
root.mainloop()

Re: Python - графика

Добавлено: 04 янв 2019, 15:40
Olej
Olej писал(а): Вывод внутренних представлений изображений масссивами numpy (и OpenCV)...
Вывод тех же внутренних представлений изображений, но так как это принято в Pillow (PIL).
Это, должно быть, проще ... но тут выяснилась сложность, с которой я бодался довольно долго:
В большинстве Linux дистрибутивов, которые я пересмотрел:

Код: Выделить всё

$ python3
Python 3.7.2rc1 (default, Dec 12 2018, 06:25:49) 
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import PIL
>>> PIL.__version__
'5.3.0'
>>> from PIL import Image
>>> from PIL import ImageTk
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: cannot import name 'ImageTk'
>>> 

Нет там в пакете Pillow модуля ImageTk, который необходим tkinter !
(я не знаю как это выглядит в каком-то говённом Windows, для которого в первую лчередь разрабатывали изначально Pillow/PIL, но в Linux это именно так)
Проблема здесь не в дистрибутиве или версиях (Python и Pillow), а в том, что пакет Pillow в репозиториях PyPI собран с опциями: --dsable-tcl и --disable-tk (как это описывается здесь) — модуля ImageTk нет и он не может быть импортирован. Для того, чтобы исправить ситуацию нужно переустановить Pillow сборкой из исходников - Pillow 5.4.0:

Код: Выделить всё

$ ls -l Pillow*
-rw-r--r-- 1 olej olej 15929265 янв  3 16:36 Pillow-5.4.0.tar.gz

$ tar -zxf Pillow-5.4.0.tar.gz 

$ du -hs Pillow-5.4.0 
30M	Pillow-5.4.0
Сборка и установка (опции --enable-tcl и --enable-tk установлены по умолчанию если их не запрещать):

Код: Выделить всё

$ cd Pillow-5.4.0

$ time sudo python3 setup.py build_ext install
running build_ext
building 'PIL._imaging' extension
creating build
creating build/temp.linux-x86_64-3.7
creating build/temp.linux-x86_64-3.7/src
creating build/temp.linux-x86_64-3.7/src/libImaging
...
Processing Pillow-5.4.0-py3.7-linux-x86_64.egg
Copying Pillow-5.4.0-py3.7-linux-x86_64.egg to /usr/local/lib/python3.7/dist-packages
Adding Pillow 5.4.0 to easy-install.pth file

real	0m17,559s
user	0m15,633s
sys	0m1,710s
После чего:

Код: Выделить всё

$ python3
Python 3.7.2rc1 (default, Dec 12 2018, 06:25:49) 
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import PIL
>>> PIL.__version__
'5.4.0'
>>> from PIL import Image
>>> from PIL import ImageTk
У нас изменилась версия Pillow, но это не суть важно, важно то, что теперь модуль ImageTk присутствует!

Re: Python - графика

Добавлено: 04 янв 2019, 15:45
Olej
Olej писал(а):У нас изменилась версия Pillow, но это не суть важно, важно то, что теперь модуль ImageTk присутствует!
Отображение Pillow форматов - pol2tk.py:

Код: Выделить всё

#!/usr/bin/python3 
# -*- coding: utf-8 -*- 
import tkinter as tk 
from tkinter import filedialog
from PIL import Image, ImageTk

root = tk.Tk()                                           # main frame
root.geometry( '600x400' )
#root.geometry('600x400+200+100')
#root.resizable( width = False, height = False )
root.title( 'Изображение' )
b1 = tk.Button( root )                                   # image frame
b2 = tk.Button( root, text = 'Открыть' )

def showImage():
    options = {                                          # define options for opening
        'title'     : "Choose an image file",
        'filetypes' : [ ( "Image", ("*.jpg","*.png","*.gif" ) ), 
                        ( "Other", ("*.ppm","*.pgm","*.bmp" ) ), 
                        ( "All files", "*.*" ) ]
              }
    file_name = filedialog.askopenfilename( **options )  # select image file
    global b1
    b1.destroy()
    print( 'image file: {}'.format( file_name ) )
#    print( root.geometry() )
    try:                                                 # load image as is ( .jpg, .png, .gif, ... )
        image = Image.open( file_name )
    except Exception as err:
        print( 'error: {}'.format( err ) )
        return
    print( '{} ({}, {})'.format( type( image ), 
           image.height, image.width, type( image.getpixel( ( 0, 0 ) ) ) ) )  
    print( '(0,0)=>{}'.format( image.getpixel( ( 0, 0 ) ) ) )    
    tkimg = ImageTk.PhotoImage( image )
    print( 'height={} width={}'.format( tkimg.height(), tkimg.width() ) )
    b1 = tk.Button( root, image = tkimg )
    b1.image = tkimg                                     # store a reference to the image as an attribute of the widget
    b1.pack( side = 'top' )
    return    

b2[ 'command' ] = showImage                              # b.bind( '<Button-1>', showImage )
b2.pack( side='bottom' )
root.mainloop()

Re: Python: Tkinter GUI

Добавлено: 21 янв 2019, 19:10
Olej
Olej писал(а): Возникла такая интересная задача... :
- сформированные программами распознавания лиц на Python (см. распознавание лиц) выводить в окна-фреймы-виджеты GUI приложений...
- это должно быть на Python 3 (не Python 2).

P.S. Я по этому поводу даже специальное руководство написал для заказчика ... поскольку это не оговаривалось и не запрещалось - приложу его сюда, может кому-то пригодится.
Как оказывается, Tkinter очень продуктивный инструмент для быстрой разработки, как и сам Python ... именно для быстрой разработки заметно интереснее, чем альтернативные PyQt или PyGTK.
Единственно, по Python + Tkinter документации (по API) в десятки раз меньше, чем по другим инструментам графики.
Поэтому собираем эффективные (не "для чайников") ресурсы по API Tkinter:

tkinter — Python interface to Tcl/Tk - в выпадающем списке там можете выбрать конкретную версию Python для уточнения.

An Introduction to Tkinter (Work in Progress)

Tkinter 8.5 reference: a GUI for Python

Курс по библиотеке Tkinter языка Python

Если эти страницы открыть в браузере на время написания Python/Tkinter кода, то этого, кажется, вполне достаточно в качестве онлай-справки для написания кода.

Re: Python - графика

Добавлено: 23 янв 2019, 17:03
Olej
Olej писал(а):Возникла такая интересная задача... :
Продолжение задачи...
- нужно из видеопотока нарезать отдельные изображения лиц, для заполнения каталога эталонных изображений в распознавании лиц...
- видеопотоком может быть, на выбор, альтернативно: а). WEB камера, одна из нескольких на выбор, б). записанный видеофайл .avi/.mkv, в). каталог с линейным набором файлов-картинок .jpg/.png/.gif/.ppm/.pgm/.bmp ...
- нужно простое и быстрое в работе приложение для таких целей.

Код: Выделить всё

#!/usr/bin/python3 
# -*- coding: utf-8 -*- 
import tkinter as tk 
from tkinter import filedialog
from PIL import Image, ImageTk
import threading
import numpy as np
import sys, os, time, argparse 
from common import *

parser = argparse.ArgumentParser()                            # construct the argument parse and parse the arguments
parser.add_argument( '-i', '--image', required = False, help = 'video stream source' )
parser.add_argument( '-k', '--known', required = False,  help = 'known persons directory' )
parser.add_argument( '-v', '--verbose', action = 'count', help = 'increase output verbosity' )

args = vars( parser.parse_args() )
debug = set_debug( args[ 'verbose' ] )                        # verbose level
if debug > 1: print( args )
if debug: print( get_version() )
dir_name = './'                                               # known faces directory
if args[ 'known' ] != None:
    dir_name = args[ 'known' ]
    if not os.path.isdir( dir_name ):
        print( 'illegal directory: '.format( dir_name ) )
        sys.exit( 1 )

root = tk.Tk()                                                # main frame
root.geometry( '640x480+200+100' )
root.resizable( width = False, height = False )
root.title( 'Select images, ' + get_version() )
c1 = tk.Canvas( root ) 

lock = threading.Semaphore()
finish = False                                                # quit flag
image = None

def runImage():
    global lock, finish, image 
    sourcer = get_source( args[ 'image' ] )                   # get images sourcer 
    first = True
    try:                                                      # while exit...
        while not finish:
            lock.acquire()
            ret, frame, delay = sourcer()                     # grab a single frame 
            lock.release()         
            if not ret: break                                 # end of stream 
            if not isinstance( frame, np.ndarray ):           # wrong format file 
                continue   
            if finish: break
            if 2 == frame.ndim:                               # gray scale to BGR
                frame = cv2.cvtColor( frame, cv2.COLOR_GRAY2BGR )
            if first and debug > 0: 
                height = np.size( frame, 0 ); width = np.size( frame, 1 ) 
                print( 'OpenCV: w={} , h={}'.format( width, height ) )
            frame = cv2.cvtColor( frame, cv2.COLOR_BGR2RGB )  # convert from BGR to RGB
            image = Image.fromarray( frame, 'RGB' )           # PIL image       
            if finish: break
            if first and debug > 0:
                height = image.height; width = image.width
                print( 'PIL: w={} , h={}'.format( width, height ) )
            tkimg = ImageTk.PhotoImage( image )               # Tk image
            if finish: break
            if first and debug > 0: 
                print( 'PhotoImage: w={} , h={}'.format( tkimg.width(), tkimg.height() ) )
            c1.winfo_toplevel().geometry( '{}x{}+200+100'.format( tkimg.width(), tkimg.height() + 50 ) )
            if finish: break
            c1.create_image( tkimg.width() / 2, tkimg.height() / 2, image = tkimg )
            c1.pack( side = 'top', fill = 'both', expand = True ) 
            if finish: break
            time.sleep( delay / 1000. )
            first = False
    except ( AttributeError, RuntimeError, KeyboardInterrupt, Exception ) as error: 
        lock.release()
        if debug > 1: print( 'thread function exception' ) 
        return 
    else:
        if debug > 1: print( 'thread function break' ) 
        return 

def on_delete():
    global dir_name
    options = {                                               # define options for deleting
        'title'       : 'Delete image file',
        'filetypes'   : [ ( "Image", ("*.jpg","*.png","*.gif" ) ), 
                          ( "Other", ("*.ppm","*.pgm","*.bmp" ) ), 
                          ( "All files", "*.*" ) ],
              }
    title = filedialog.askopenfilename( **options )
    if 0 == len( title ): return                              # cancel
    n = title.rfind( os.sep ) 
    dir_name = title[ 0:n ]
    os.remove( title )

b2 = tk.Button( root, text = 'Delete image' )
b2.pack( side = 'bottom' )
b2[ 'command' ] = on_delete                                   # b2.bind( '<Button-1>', on_delete )

file_name = '.jpg'
def on_save():
    global file_name, dir_name, lock 
    lock.acquire()
    options = {                                               # define options for saving
        'title'       : 'Save image as file...',
        'filetypes'   : [ ( "Image", ("*.jpg","*.png","*.gif" ) ), 
                          ( "Other", ("*.ppm","*.pgm","*.bmp" ) ), 
                          ( "All files", "*.*" ) ],
        'initialfile' : file_name,
        'initialdir'  : dir_name 
              }
    title = filedialog.asksaveasfilename( **options )         # select image file
    if 0 == len( title ):                                     # cancel
        lock.release()
        return
    n = title.rfind( os.sep )
    dir_name, file_name = title[ 0:n ], title[ n + 1: ]
    if debug > 1: print( '{} => {} | {}'.format( title, dir_name, file_name ) )
    try: 
        image.save( title )
    except ( ValueError, Exception ) as error:
        print( "error: image file can't save: {}!".format( error ) )
    lock.release()

b1 = tk.Button( root, text = '   Save as...   ' )
b1.pack( side = 'bottom' )
b1[ 'command' ] = on_save                                     # b1.bind( '<Button-1>', on_save )

sys.setswitchinterval( 1 )
thread = threading.Thread( target = runImage )
thread.start()                                                # visualisation video ... 

def on_closing():                                             # exit application 
    global finish
    print( ' ... waiting ...' )
    lock.acquire()
    t1 = time.strftime( '%H:%M:%S' )
    finish = True
    root.destroy()
    thread.join( timeout = 3 )    
    t2 = time.strftime( '%H:%M:%S' )
    if debug > 1: 
        print( '{} ... {} - thread is running: {}'.format( t1, t2, thread.is_alive() ) )
    if( thread.is_alive() ):
        raise KeyboardInterrupt( 'thread not finished: press ^C' )
    lock.release()
    sys.exit() 

def on_closing1():
    lock.acquire()
    on_closing()

root.protocol( 'WM_DELETE_WINDOW', on_closing )               # winclose interrupt
root.bind( '<Escape>', lambda e: on_closing() )               # Esc interrupt
try:
    root.mainloop()
except KeyboardInterrupt:                                     # ^C interrupt 
    if debug > 1: print( '\n***************' )
    on_closing()
Сложность и 1/3 кода свяязаны с тем, что нужно корректно остановить, завершить приложение, и сделать это нужно асинхронно - одновременно с отображением видео в окно.

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/AplitSoft/FaceDL/fred$ ./fred.py -h
usage: fred.py [-h] [-i IMAGE] [-k KNOWN] [-v]

optional arguments:
  -h, --help            show this help message and exit
  -i IMAGE, --image IMAGE
                        video stream source
  -k KNOWN, --known KNOWN
                        known persons directory
  -v, --verbose         increase output verbosity
Из потока -i мы набиваем отдельными изображениями-файлами каталог -k.

Re: Python: Tkinter GUI

Добавлено: 26 янв 2019, 17:34
Olej
Olej писал(а): Продолжение задачи...
Единственно нехорошая особенность этого кода - он написан в императивном, структурном стиле + пришлось чередовать операторы главной ветви программы и описания используемых функций ... об этом я зафиксировал даже в отдельной теме: Python: предварительное определение функций. Потом, позже, работать с модификацией такого кода будет сплошным гемороем.
Выход - переписать это в объектном стиле:

Код: Выделить всё

#!/usr/bin/python3 
# -*- coding: utf-8 -*- 
import tkinter as tk 
from tkinter import filedialog
from PIL import Image, ImageTk
import threading
import numpy as np
import sys, os, time, argparse 
from common import *

parser = argparse.ArgumentParser()                                      # construct the argument parse 
parser.add_argument( '-i', '--image', required = False, help = 'video stream source' )
parser.add_argument( '-k', '--known', required = False,  help = 'known persons directory' )
parser.add_argument( '-v', '--verbose', action = 'count', help = 'increase output verbosity' )
args = vars( parser.parse_args() )
debug = set_debug( args[ 'verbose' ] )                                  # verbose level
if debug: print( '{}, verbose level {}'.format( get_version(), debug ) )
                                                         
class Application( tk.Frame ):
    global debug
    dir_name = './'                                                     # known faces directory
    file_name = '.jpg'                                                  # last image name  
    lock = threading.Semaphore()
    finish = False                                                      # finish thread flag
    image = None; thread = None
      
    def __init__( self, args ):
        if debug > 1: print( args )
        if args[ 'known' ] != None:
            dir_name = args[ 'known' ]
            if not os.path.isdir( dir_name ):
                print( 'illegal directory: '.format( dir_name ) )
                sys.exit( 1 )
        tk.Frame.__init__( self, master = None )
        root = self.winfo_toplevel()                                    # main window
        root.protocol( 'WM_DELETE_WINDOW', self.on_closing )            # winclose interrupt
        root.bind( '<Escape>', lambda e: self.on_closing() )            # Esc interrupt        
        root.geometry( '640x480+200+100' )
        root.resizable( width = False, height = False )
        root.title( 'Select images, ' + get_version() )
        root.config( padx = 10, pady = 10 )
        self.pack()
        sys.setswitchinterval( 1 )
        self.thread = threading.Thread( target = self.runImage )
        self.thread.start()                                             # visualisation video ... 
        self.c1 = tk.Canvas( self )                                     # canvas for image      
        if debug > 2: print( 'Canvas: {}'.format( self.c1.keys() ) ) 
        self.b1 = tk.Button( self, text = 'Delete image' )
        if debug > 2: print( 'Button: {}'.format( self.b1.keys() ) )
        self.b1[ 'command' ] = self.on_delete 
        self.b1.pack( side = 'bottom' )                
        self.b2 = tk.Button( self, text = '   Save as...   ' )
        self.b2[ 'command' ] = self.on_save  
        self.b2.pack( side = 'bottom' )

    def on_delete( self ):
        self.b1.config( relief = tk.SUNKEN )
        options = {                                                     # define options for deleting
            'title'       : 'Delete image file',
            'filetypes'   : [ ( "Image", ("*.jpg","*.png","*.gif" ) ), 
                              ( "Other", ("*.ppm","*.pgm","*.bmp" ) ), 
                              ( "All files", "*.*" ) ],
                  }
        title = filedialog.askopenfilename( **options )
        if len( title ) != 0:                                           
            n = title.rfind( os.sep ) 
            self.dir_name, file_name = title[ 0:n ], title[ n + 1: ]
            os.remove( title )
            if debug > 0:
                print( 'file {} delete from directory {}'.format( file_name, self.dir_name ) )
        self.b1.config( relief = tk.RAISED )

    def on_save( self ):
        self.b2.config( relief = tk.SUNKEN )
        self.lock.acquire()
        options = {                                                     # define options for saving
            'title'       : 'Save image as file...',
            'filetypes'   : [ ( "Image", ("*.jpg","*.png","*.gif" ) ), 
                              ( "Other", ("*.ppm","*.pgm","*.bmp" ) ), 
                              ( "All files", "*.*" ) ],
            'initialfile' : self.file_name,
            'initialdir'  : self.dir_name 
                  }
        title = filedialog.asksaveasfilename( **options )               # select image file
        if len( title ) != 0:                                           # cancel
            n = title.rfind( os.sep )
            self.dir_name, self.file_name = title[ 0:n ], title[ n + 1: ]
            if debug > 1: 
                print( '{} => {} | {}'.format( title, self.dir_name, self.file_name ) )
            try: 
                self.image.save( title )
            except ( ValueError, Exception ) as error:
                print( "error: image file can't save: {}!".format( error ) )
            else:
                if debug > 0:
                    print( 'file {} add to directory {}'.format( self.file_name, self.dir_name ) )
        self.lock.release()
        self.b2.config( relief = tk.RAISED )
    
    def on_closing( self ):                                             # exit application 
        print( ' ... waiting ...' )
        self.lock.acquire()
        t1 = time.strftime( '%H:%M:%S' )
        self.finish = True
        self.winfo_toplevel().destroy()
        self.thread.join( timeout = 3 )    
        t2 = time.strftime( '%H:%M:%S' )
        if debug > 1: 
            print( '{} ... {} - thread is running: {}'.format( t1, t2, self.thread.is_alive() ) )
        if( self.thread.is_alive() ):
            raise KeyboardInterrupt( 'thread not finished: press ^C' )
        self.lock.release()
        self.quit()

    def runImage( self ):
        sourcer = get_source( args[ 'image' ] )                         # get images sourcer 
        first = True
        try:             
            while not self.finish:                                      # while not exit...
                self.lock.acquire()
                ret, frame, delay = sourcer()                           # grab a single frame 
                self.lock.release()         
                if not ret: break                                       # end of stream 
                if not isinstance( frame, np.ndarray ):                 # wrong format file 
                    continue   
                if self.finish: break
                if 2 == frame.ndim:                                     # gray scale to BGR
                    frame = cv2.cvtColor( frame, cv2.COLOR_GRAY2BGR )
                if first and debug > 1: 
                    height = np.size( frame, 0 ); width = np.size( frame, 1 ) 
                    print( 'OpenCV: w={} , h={}'.format( width, height ) )
                frame = cv2.cvtColor( frame, cv2.COLOR_BGR2RGB )        # convert from BGR to RGB
                self.image = Image.fromarray( frame, 'RGB' )            # PIL image       
                if self.finish: break
                if first and debug > 1:
                    height = self.image.height; width = self.image.width
                    print( 'PIL: w={} , h={}'.format( width, height ) )
                tkimg = ImageTk.PhotoImage( self.image )                # Tk image
                if self.finish: break
                if first and debug > 1: 
                    print( 'PhotoImage: w={} , h={}'.format( tkimg.width(), tkimg.height() ) )
                self.c1.winfo_toplevel().geometry( '{}x{}+200+100'.format( tkimg.width(), tkimg.height() + 70 ) )
                self.c1.configure( width = tkimg.width(), height = tkimg.height() )
                if self.finish: break
                self.c1.create_image( tkimg.width() / 2, tkimg.height() / 2, image = tkimg )
                self.c1.pack( side = 'top', fill = 'both', expand = True ) 
                if self.finish: break
                time.sleep( delay / 1000. )
                first = False
        except ( AttributeError, RuntimeError, KeyboardInterrupt, Exception ): # as error: 
            self.lock.release()
            if debug > 1: print( 'thread function exception' ) 
            return 
        else:
            if debug > 1: print( 'thread function break' ) 
            return 
                                                        
app = Application( args )
try:
    app.mainloop()
except KeyboardInterrupt:                                               # ^C interrupt 
    if debug > 1: print( '\n*************** ^C' )
    app.on_closing()
Выполнение и картинки - всё осталось точно тем же. Специально сделал полный аналог.

Re: Python - графика

Добавлено: 02 фев 2019, 15:29
Olej
Olej писал(а): Скачивается здесь (Linux): http://www.bitflipper.ca/rapyd/rapyd-1-0-2.tgz

Код: Выделить всё

olej@ACER:~/Загрузки$ ls -l rapyd-1-0-2.tgz 
-rw-r--r-- 1 olej olej 624203 фев  2 14:10 rapyd-1-0-2.tgz
Как видно, это такое минимальное творение... Разархивируем куда-нибудь (в $HOME):

Код: Выделить всё

olej@ACER:~$ ls -l rapyd-1-0-2 
итого 2448
-rw-r--r-- 1 olej olej  32603 июл  1  2014 dnd_realworld.py
-rw-r--r-- 1 olej olej  25751 фев  2 14:16 dnd_realworld.pyc
drwxr-xr-x 2 olej olej   4096 фев  2 14:13 Icons
-rw-r--r-- 1 olej olej  63263 июл  1  2014 rapyd.config
-rw-rw-r-- 1 olej olej  21048 июл  1  2014 RapydDemo.rpj
-rw-rw-r-- 1 olej olej 587514 июл  1  2014 rapyd.help
-rwxr-xr-x 1 olej olej 701951 июл  1  2014 rapyd.py
-rw-r--r-- 1 olej olej   2166 июл  1  2014 rapyd.template
-rw-r--r-- 1 olej olej   4554 июл  1  2014 README.TXT
-rw-r--r-- 1 olej olej  26685 июл  1  2014 rpErrorHandler.py
-rw-r--r-- 1 olej olej  18501 фев  2 14:16 rpErrorHandler.pyc
-rw-r--r-- 1 olej olej 196658 июл  1  2014 rpHelp.py
-rw-r--r-- 1 olej olej 141548 фев  2 14:16 rpHelp.pyc
-rw-rw-r-- 1 olej olej 143436 июл  1  2014 rpOption.py
-rw-r--r-- 1 olej olej 115862 фев  2 14:16 rpOption.pyc
-rw-r--r-- 1 olej olej 215769 июл  1  2014 rpWidgets.py
-rw-r--r-- 1 olej olej 151311 фев  2 14:16 rpWidgets.pyc
-rw-r--r-- 1 olej olej  13402 июл  1  2014 versions.txt
-rw-r--r-- 1 olej olej      0 фев  2 14:20 xlocker.dec
Видно, что это действительно минимально + само написано на Python, ©2014-2015.
Запуск непосредственно из разархивированного каталога:

Код: Выделить всё

olej@ACER:~/rapyd-1-0-2$ ./rapyd.py
...