analitics

Pages

Tuesday, May 26, 2020

Python Qt5 : PyQt5 and PyGame compatibility with source code.

This tutorial tries to solve from the objectives related to solving and stabilizing compatibility errors between PyQt4 and PyQt5 and creating a common interface between PyQt5 and PyGame.
There is always the same problem in programming when the developer for some reason has to change classes, methods and functions and reusing the old code is no longer valid.
In this case, common or other errors occur, which leads to a waste of time.
I will present a simple way to solve these problems.
I really like to use the PyQt5 module to create interfaces for my python programs and scripts.
Like any programmer who hasn't fully used all A.P.I, I always use the old source code I programmed in the past.
What the developer says about the transition from PyQt4 to PyQt5 we can see on the official page.
Obviously, you will have to move on to things to know but it is quite difficult to always come back and read this content when you have programming errors.
Today, I wanted to make a simple drawing interface in PyGame that would be included in a PyQt5 interface.
I tried to use an old code created by me in PyQt4 but I realized that I had encountered errors before switching to the new PyQt5.
This compatibility problem generates errors and can be solved as follows: by knowing the exact solution and fixing errors in real time, studying the changes created by the developer or the classic search for errors.
My solution comes with the help of these solutions and requires a simple step using the commented source code.
To show you how simple it is to understand I will show you the source code for the interface I built that simply to solves the problem of understanding compatibility by reading the developer source code with simple and useful comments.
#the old import for PyQt4
#from PyQt4 import QtGui

#the new import for PyQt5
#from PyQt5 import QtCore, QtGui, QtWidgets
#class MainWindow(QtWidgets.QMainWindow, UI.MainUI.Ui_MainWindow):

from PyQt5 import QtGui
from PyQt5 import QtWidgets
import pygame
import sys

# old definition for PyQt4 for QWidget
#class ImageWidget(QtGui.QWidget):
class ImageWidget(QtWidgets.QWidget):   
    def __init__(self,surface,parent=None):
        super(ImageWidget,self).__init__(parent)
        w=surface.get_width()
        h=surface.get_height()
        self.data=surface.get_buffer().raw
        self.image=QtGui.QImage(self.data,w,h,QtGui.QImage.Format_RGB32)

    def paintEvent(self,event):
        my_paint=QtGui.QPainter()
        # the definitions for PyQt4 and PyQt5 use QtGui.QPainter()     
        my_paint.begin(self)
        my_paint.drawImage(0,0,self.image)
        my_paint.end()
        
# old definition for PyQt4 for QMainWindow
#class MainWindow(QtGui.QMainWindow):
class MainWindow(QtWidgets.QMainWindow):
    def __init__(self,surface,parent=None):
        super(MainWindow,self).__init__(parent)
        self.setFixedSize(640, 480)
        self.setCentralWidget(ImageWidget(surface))
# this part of source code need to be updated if you want to use animation        
# init PyGame 
pygame.init()
# define a surface 
my_surface=pygame.Surface((640,480))
# fill the surface, see https://www.pygame.org/docs/ref/surface.html#pygame.Surface.fill
my_surface.fill((0,0,255,176))
# draw circle see https://www.pygame.org/docs/ref/draw.html#pygame.draw.circle
pygame.draw.circle(my_surface,(0,0,127,255),(76,76),76)
# draw ellipse (surface, color(R,G,B), size (x,y,x+dx, y+y+dy) )
pygame.draw.ellipse(my_surface,(127,0,0,0),(0,0,12,76))
 
# this part of source code will show 
# the my_surface created with PyGame in PyQt5
# old definition for PyQt4
#app=QtGui.QApplication(sys.argv)
app=QtWidgets.QApplication(sys.argv)
my_window=MainWindow(my_surface)
my_window.show()
app.exec_()

Sunday, May 24, 2020

Python 3.8.3 : A brief introduction to the Celery python package.

The development team tells us:
Celery is a simple, flexible, and reliable distributed system to process vast amounts of messages, while providing operations with the tools required to maintain such a system.
To do that Celery uses tasks.
These task queues are used as a mechanism to distribute work across threads or machines using brokers.
A task a client puts a message on the queue and the broker then delivers the message to a worker.
The install of this python package is simple:
Celery can run on a single machine, on multiple machines, or even across datacenters.
pip3 install -U Celery
Collecting Celery
...
Installing collected packages: pytz, billiard, vine, amqp, kombu, Celery
Successfully installed Celery-4.4.2 amqp-2.5.2 billiard-3.6.3.0 kombu-4.6.8 pytz-2020.1 vine-1.3.0
The Celery library must be instantiated before use like an application.
First, I tested with a simple example application from the official website.
from celery import Celery
app = Celery()

@app.task
def add(x, y): return x + y

if __name__ == '__main__':
    app.worker_main()
When I run it this application the output is this:
python.exe .\example_tasks.py
 
 -------------- celery@DESKTOP-9DSLSMF v4.4.2 (cliffs)
--- ***** ----- 
-- ******* ---- Windows-10-10.0.18362-SP0 2020-05-24 18:30:04
- *** --- * --- 
- ** ---------- [config]
- ** ---------- .> app:         __main__:0x1c697187940
- ** ---------- .> transport:   amqp://guest:**@localhost:5672//
- ** ---------- .> results:     disabled://
- *** --- * --- .> concurrency: 6 (prefork)
                .> celery           exchange=celery(direct) key=celery


[2020-05-24 18:30:06,273: ERROR/MainProcess] consumer: Cannot connect to amqp://guest:**@127.0.0.1:5672//:
[WinError 10061] No connection could be made because the target machine actively refused it.
Trying again in 2.00 seconds... (1/100)

[2020-05-24 18:30:10,296: ERROR/MainProcess] consumer: Cannot connect to amqp://guest:**@127.0.0.1:5672//:
[WinError 10061] No connection could be made because the target machine actively refused it.
Trying again in 4.00 seconds... (2/100)

[2020-05-24 18:30:16,323: ERROR/MainProcess] consumer: Cannot connect to amqp://guest:**@127.0.0.1:5672//:
[WinError 10061] No connection could be made because the target machine actively refused it.
Trying again in 6.00 seconds... (3/100)


worker: Hitting Ctrl+C again will terminate all running tasks!

worker: Warm shutdown (MainProcess)
This error is show because I trying to connect to a local instance of default broker named RabbitMQ .
RabbitMQ is most popular open source message broker and can be download it from the official webpage.
If you want to install RabbitMQ, then you need to install Erlang, see screenshot:
python RabbitMQ Erlang
This is not the only one option, you have many features like for example Redis.
The development team try experimental support for a myriad of other solutions, including using SQLite for local development.
A full list with all supported brokers can be found here.
Celery is easy to integrate with web frameworks, some of them even have integration packages:
  • Pyramid  with pyramid_celery;
  • Pylons with celery-pylons;
  • Flask not needed;
  • web2py with web2py-celery;
  • Tornado with tornado-celery;
  • Tryton with celery_tryton;
  • and Django also no not needed because works well.
Maybe I will make another tutorial about this python module and how works with Redis or Django.

Monday, May 18, 2020

News : Microsoft Build digital event for Python users.

We in the Python team are excited to be joining Microsoft Build 2020, which is starting next Tuesday, May 19. During a global pandemic, Build has turned into a digital-only event, running for 48 continuous hours, that is open to anyone around the world, at no cost.

At Build, we’ll demo the new capabilities we’ve been building recently. We’ll also showcase the work we have been doing to enable Python developers to build applications and analyze data using Visual Studio Code. Lastly, we’ll show how you can run Python web apps and machine learning models on the cloud with Azure.

Our friends across Microsoft will also present services and tools that developers, including those working with Python, can leverage to remain productive and continue collaborating even in remote, distributed teams.

You can participate in a live Q&A stream session:
This event can be found at the official webpage.

Sunday, May 17, 2020

Python 3.8.3 : Simple example to fix maximum recursion depth exceeded.

This short tutorial try to solve simple and easy the stack limit for recursion without using advanced programming techniques.
sys.setrecursionlimit(limit)
    Set the maximum depth of the Python interpreter stack to limit. 
This limit prevents infinite recursion from causing an overflow of the C stack
and crashing Python.
This can be done in most cases with:
import sys
sys.setrecursionlimit(1976)
Let's test with an simple example for a given number.
input_number = 1234567890987654321
I create a script for the next function to print the result:
def split_input(n):
    return split_input(n // 10) + [n % 10]
The result of this function is:
python flow_001.py
Traceback (most recent call last):
  File "flow_001.py", line 12, in 
    input_list = split_input(input_number)
  File "flow_001.py", line 10, in split_input
    return split_input(n // 10) + [n % 10]
  File "flow_001.py", line 10, in split_input
    return split_input(n // 10) + [n % 10]
  File "flow_001.py", line 10, in split_input
    return split_input(n // 10) + [n % 10]
  [Previous line repeated 996 more times]
RecursionError: maximum recursion depth exceeded
You can see I got an error. Let fix this function with a proper source code:
def split_nr(n):
    if n < 1:
        return [n]
    else:
        return split_nr(n // 10) + [n % 10] 
This will solve the recursion split function.
python flow_001.py
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 9, 8, 7, 6, 5, 4, 3, 2, 1]

Friday, May 15, 2020

IronPython 2.7.7 : Intro on RevitPythonShell.

This is one of the several ways to use RevitPythonWrapper:
  • pyRevit;
  • RevitPythonShell;
  • Dynamo;
This tool help you to use python with the Revit tool and can be found at the GitHub webpage.
They say:
The RevitPythonShell adds an IronPython interpreter to Autodesk Revit and Vasari. The RevitPythonShell (RPS) lets you to write plugins for Revit in Python, my favourite scripting language! But even better, it provides you with an interactive shell that lets you see the results of your code as you type it. This is great for exploring the Revit API while writing your Revit Addins - use this in combination with the RevitLookup database exploration tool to become a Revit API Ninja :)
You need to install your instaler according with the Revit version.
You can start it from Main Menu - Add-Ins and you see it.
Is very easy to use it, let's see one simple example:
import clr
clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import BuiltInCategory as Bic
from Autodesk.Revit.DB import FilteredElementCollector as Fec
from Autodesk.Revit.DB import Transaction
# reference to the current open revit model is
doc = __revit__.ActiveUIDocument.Document
Let's see a simple example on Revit with an wall and a simple door.
IronPython 2.7.7 (2.7.7.0) on .NET 4.0.30319.42000 (64-bit)
Type "help", "copyright", "credits" or "license" for more information.
>>> a = b = 1 
>>> c = a + b + 1
>>> print(c)
3
>>> from Autodesk.Revit.DB import FilteredElementCollector as Fec
>>> from Autodesk.Revit.DB import BuiltInCategory as Bic
>>> doors = Fec(doc).OfCategory(Bic.OST_Doors).WhereElementIsNotElementType().ToElements()
>>> print(dir(doors))
['Add', 'AddRange', 'AsReadOnly', 'BinarySearch', 'Capacity', 'Clear', 'Contains', 'ConvertAll', 'CopyTo',
'Count', 'Enumerator', 'Equals', 'Exists', 'Find', 'FindAll', 'FindIndex', 'FindLast', 'FindLastIndex', 
'ForEach', 'GetEnumerator', 'GetHashCode', 'GetRange', 'GetType', 'IndexOf', 'Insert', 'InsertRange', 
'IsReadOnly', 'IsSynchronized', 'Item', 'LastIndexOf', 'MemberwiseClone', 'ReferenceEquals', 'Remove', 
'RemoveAll', 'RemoveAt', 'RemoveRange', 'Reverse', 'Sort', 'SyncRoot', 'ToArray', 'ToString', 'TrimExcess', 
'TrueForAll', '__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__format__', 
'__getattribute__', '__getitem__', '__hash__', '__init__', '__iter__', '__len__', '__new__', 
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', 
'__str__', '__subclasshook__']
>>> for each_door in doors:
... 	print(each_door.Id)
... 	print(each_door.Mirrored)
... 	print(each_door.Symbol.LookupParameter("Type Name").ToString())
... 
840590
False
Autodesk.Revit.DB.Parameter
>>> 
Let's see the output of this code on Revit.


Thursday, May 14, 2020

Python 3.8.3 : Pyxel free game engine.

Pyxel is a free game engine is build for create old fashioned pixel art style games easily.
Pyxel is published under MIT License.
This allow you to use 2D sprites, sound and interactions.
The project can be found at the GitHub webpage.
The basic features are:
  • Run on Windows, Mac, and Linux
  • Code writing with Python3
  • Fixed 16 color palette
  • 256x256 sized 3 image banks
  • 256x256 sized 8 tilemaps
  • 4 channels with 64 definable sounds
  • 8 musics which can combine arbitrary sounds
  • Keyboard, mouse, and gamepad inputs
  • Image and sound editor
The instalation is easy.
pip3 install pyxel
Collecting pyxel
...
Successfully installed altgraph-0.17 pefile-2019.4.18 pyinstaller-3.6 pywin32-ctypes-0.2.0 pyxel-1.3.7
Use this command to start the tool for create sprites and sounds.
pyxeleditor
You can test many examples on GitHub.
The basic example from the web is simple:
import pyxel

class App:
    def __init__(self):
        pyxel.init(160, 120, caption="Hello Pyxel")
        pyxel.image(0).load(0, 0, "assets/pyxel_logo_38x16.png")
        pyxel.run(self.update, self.draw)

    def update(self):
        if pyxel.btnp(pyxel.KEY_Q):
            pyxel.quit()

    def draw(self):
        pyxel.cls(0)
        pyxel.text(55, 41, "Hello, Pyxel!", pyxel.frame_count % 16)
        pyxel.blt(61, 66, 0, 0, 0, 38, 16)

App()

I start with a simple example. I don't find a collision system on Pyxel. Let's see the source code:
import pyxel
from pyxel import circ, cls, flip, init
from random import randint

# the position of the ball
x = y = 30  
# the speed of the ball
v = w = 3  
# create the screen as 160x120 size
pyxel.init(160, 112)  
#
data = [70, 60, 30, 70]


 # draw a line below the bar chart

while True:
    # erase the screen with color number 1 (blue)
    pyxel.cls(1)  

    # process the movement of the ball
    x += v
    y += w
    
    r = randint(0, 160)  
    a = randint(0, 112)
    rr = randint(0, 160) 
    aa = randint(0, 112)
    # create random lines on screen
    pyxel.line(a, aa, r, rr, 5)

    # set the border
    if x <= 7 or x >= 160:
        x = min(max(x, 7), 160)
        v = -v

    if y <= 7 or y >= 112:
        y = min(max(y, 7), 112)
        w = -w
  
    # draw the ball with different colors
    pyxel.circ(x, y, 4, pyxel.frame_count % 8) 
 
    # create a simple chart
    for i, d in enumerate(data):
        pyxel.rect(i * 33 + 10, 120 - d, 10, d, 8 + i) 

    # draw the game
    pyxel.flip()

Tuesday, May 12, 2020

Python 3.8.3 : Create shortcuts and add python to the context menu.

The tutorial for today is a simple script for python user.
If you run this python script in your folder you will get an python shortcut to the python and the python to the Context Menu in Windows 10.
The Context Menu can be used with right click from your mouse, see the screenshot.

This script can also be used with any executable if you make some changes.
import os
import pythoncom
from win32com.shell import shell

dirpath = os.getcwd()
if not os.path.exists(dirpath):
    os.makedirs(dirpath)

win_user = os.getenv('username')
shortcut_path = dirpath + r"\python.lnk"

shortcut_path = str(r"C:\\Users\\")+win_user+str("\\AppData\\Roaming\\Microsoft\\Windows\\SendTo\\python.lnk")

target_path = r"D:\\Python38_64\\python.exe"

shortcut_instance = pythoncom.CoCreateInstance(
    shell.CLSID_ShellLink, None, pythoncom.CLSCTX_INPROC_SERVER, shell.IID_IShellLink)
persist_file = shortcut_instance.QueryInterface(pythoncom.IID_IPersistFile)
shortcut_instance.SetPath(target_path)
persist_file.Save (shortcut_path, 0)

Monday, May 11, 2020

Python Qt5 : Simple text editor with QPlainTextEdit.

I haven't played python in a long time. It can be seen after the last article.
Today I installed Python 3.8.3 and my favorite PyQt5 and started to see how much I forgot from what I knew.
Installing PyQt5 python mode was simple.
pip3 install PyQt5
Collecting PyQt5
  Downloading PyQt5-5.14.2-5.14.2-cp35.cp36.cp37.cp38-none-win_amd64.whl (52.9 MB)
     |████████████████████████████████| 52.9 MB 80 kB/s
Collecting PyQt5-sip<13>=12.7
  Downloading PyQt5_sip-12.7.2-cp38-cp38-win_amd64.whl (59 kB)
     |████████████████████████████████| 59 kB 4.1 MB/s
Installing collected packages: PyQt5-sip, PyQt5
Successfully installed PyQt5-5.14.2 PyQt5-sip-12.7.2

The next step was to use QPlainTextEdit so the task was simple: an editor.
Because I created another article about a publisher, see this tutorial, I decided to attach a main menu with submenus and customize it.
The source code is simple to understand and is commented for a better understanding.
# -*- coding: utf-8 -*-
"""
@author: catafest
"""
import sys
import sys, os
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QMainWindow, QAction, qApp, QApplication, QDesktopWidget
from PyQt5.QtWidgets import QMenu, QPlainTextEdit, QSizePolicy
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt,QSize

class Example(QMainWindow):
    #init the example class to draw the window application    
    def __init__(self, parent = None):
        super(Example,self).__init__(parent)  
        size_policy = self.sizePolicy()
        size_policy.setHeightForWidth(True)
        size_policy.setVerticalPolicy(QSizePolicy.Preferred)
        self.setSizePolicy(size_policy)  
        self.initUI()

    #create the def center to select the center of the screen         
    def center(self):
        # geometry of the main window
        qr = self.frameGeometry()
        
        # center point of screen
        cp = QDesktopWidget().availableGeometry().center()
        # move rectangle's center point to screen's center point
        qr.moveCenter(cp)
        # top left of rectangle becomes top left of window centering it
        self.move(qr.topLeft())

    #create the init UI to draw the application
    def initUI(self):               
        #create the action for the exit application with shortcut and icon
        #you can add new action for File menu and any actions you need
        #self.setMinimumSize(QSize(440, 240))    
        self.setWindowTitle("parse sensors output - catafest")
        self.editor_text = QPlainTextEdit(self)
        # font
        default_font = self.editor_text.font()
        default_font.setPointSize(9)
        self.editor_text.setFont(default_font)

        self.setCentralWidget(self.editor_text)
        #self.editor_text.setMinimumSize(400,600)

        self.editor_text.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
        self.editor_text.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
        
        self.editor_text.setObjectName("editor_text")
        self.editor_text.setEnabled(True)
        
        openAct = QAction(QIcon('open.png'), '&Open', self)        
        openAct.setShortcut('Ctrl+O')
        openAct.setStatusTip('Open file')
        openAct.triggered.connect(self.my_OpenDialog)

        exitAct = QAction(QIcon('exit.png'), '&Exit', self)        
        exitAct.setShortcut('Ctrl+Q')
        exitAct.setStatusTip('Exit application')
        exitAct.triggered.connect(qApp.quit)
        #create the status bar for menu 
        self.statusBar()
        #create the menu with the text File , add the exit action 
        #you can add many items on menu with actions for each item
        menubar = self.menuBar()
        fileMenu = menubar.addMenu('&File')
        # create open 
        fileMenu.addAction(openAct)
        # create exit 
        fileMenu.addAction(exitAct)
        
        # add submenu to menu 
        submenu = QMenu('Submenu',self)

        # some dummy actions
        submenu.addAction('Submenu 1')
        submenu.addAction('Submenu 2')
        
        # allow adding a sphere
        meshMenu = submenu.addMenu('Mesh')
        self.add_sphere_action = QAction('Add Sphere', self)
        self.add_sphere_action.triggered.connect(self.add_sphere)
        meshMenu.addAction(self.add_sphere_action)

        # add to the top menu
        menubar.addMenu(submenu)

        #layout = QtWidgets.QGridLayout(self)
        #layout.addWidget(self.editor_text)
        self.editor_text

        #resize the window application 
        self.resize(960, 720)
        #draw on center of the screen 
        self.center()
        #add title on windows application 
        self.setWindowTitle('Simple menu')
        #show the application
        self.show()
        #close the UI class

    def my_OpenDialog(self):
        path = QtWidgets.QFileDialog.getOpenFileName(
            self, 'Open file', '',
            'Text files (*.txt);;HTML files (*.html)')[0]
        if path:
            file = QtCore.QFile(path)
            if file.open(QtCore.QIODevice.ReadOnly):
                stream = QtCore.QTextStream(file)
                text = stream.readAll()
                info = QtCore.QFileInfo(path)
                if info.completeSuffix() == 'txt':
                    #self.editor_text.setHtml(text)
                    self.editor_text.insertPlainText(text)
                else:
                    self.editor_text.setPlainText(text)
                file.close()

    def add_sphere(self):
        """ add text with sphere """
        print("sphere")
        
if __name__ == '__main__':
    #create the application 
    app = QApplication(sys.argv)
    #use the UI with new  class
    ex = Example()
    #run the UI 
    sys.exit(app.exec_())