Monday, May 31, 2021

Setting up PyQT5 && Mariadb CRUD Application on Fedora Server 34

Code posted below is actually a kind of POC and may be freely modified any way you like. It is testing standard connection setup Python 3.9.5 to native Fedora's Mariadb-Server running on Server Edition Fedora 34 with Deepin Desktop installed. Here I have to notice that KDE Plasma 5.20 Desktop instance on the same server seems to be a better choice for PyQT5  windowing running CRUD application with any of Mariadb-Server 10.5 or PostgreSQL 13.2 .

First grant remote access to database for root :-

[boris@fedora33server ~]$ mysql -u root -p

Enter password: 

Welcome to the MariaDB monitor.  Commands end with ; or \g.

Your MariaDB connection id is 18

Server version: 10.5.10-MariaDB MariaDB Server

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> GRANT ALL PRIVILEGES ON *.* TO 'root'@'192.168.%' IDENTIFIED BY '*******' WITH GRANT OPTION;

Query OK, 0 rows affected (0.001 sec)

MariaDB [(none)]> FLUSH PRIVILEGES ;

Query OK, 0 rows affected (0.000 sec)

Code itself

#!/usr/bin/python3

import sys

import mariadb 

from PyQt5.QtWidgets import QTableWidget, QApplication, QMainWindow, QTableWidget 

from PyQt5.QtWidgets import QTableWidgetItem, QWidget, QPushButton, QLineEdit

from PyQt5 import QtGui

class MyWidget(QWidget):

    def __init__(self):

        super().__init__()

        self.con()

        self.setGeometry(100, 100, 500, 600)

        self.setWindowTitle('Scores List')

        self.tb = Tb(self)

        self.btn = QPushButton('Refresh View', self)

        self.btn.resize(150, 40)

        self.btn.move(300, 10)

        self.btn.clicked.connect(self.upd)

        self.idp = QLineEdit(self)

        self.idp.resize(150, 40)

        self.idp.move(300, 60)

        self.idp.setReadOnly(True)

        self.fio = QLineEdit(self)

        self.fio.resize(150, 40)

        self.fio.move(300, 110)

        self.oce = QLineEdit(self)

        self.oce.resize(150, 40)

        self.oce.move(300, 160)

        self.btn = QPushButton('Add row', self)

        self.btn.resize(150, 40)

        self.btn.move(300, 210)

        self.btn.clicked.connect(self.ins)

        self.btn = QPushButton('Update row', self)

        self.btn.resize(150, 40)

        self.btn.move(300, 260)

        self.btn.clicked.connect(self.change)

        self.btn = QPushButton('Delete row', self)

        self.btn.resize(150, 40)

        self.btn.move(300, 310)

        self.btn.clicked.connect(self.dels)

# local connect to mariadb datatabase 

    def con(self):

        self.conn = mariadb.connect(user = "root",

                              password = "**********",

                              host = "192.168.0.XX",

                              port = 3306,

                              database = "db1")

        self.cur = self.conn.cursor()

    def upd(self):   # insert record into table

        self.conn.commit()

        self.tb.updt()

        self.idp.setText('')

        self.fio.setText('')

        self.oce.setText('')

    def ins(self):

        fio, oce = self.fio.text(), self.oce.text()

        try:

            self.cur.execute("insert into students (name, ocenka) values (%s,%s)",(fio,oce))

        except:

            pass

        self.upd()

  def change(self):   # update record in table

        fio, oce = self.fio.text(), self.oce.text()

        try:

            ids = int(self.idp.text()) 

        except:

            return

        self.cur.execute("update students set name=%s,ocenka=%s where id=%s",(fio,oce,ids,))

        self.upd()

# delete record from table 

    def dels(self):

        try:

            ids = int(self.idp.text()) # row identifier

        except:

            return

        self.cur.execute("delete from students where id=%s",(ids,))

        self.upd()

class Tb(QTableWidget):

    def __init__(self, wg):

        self.wg = wg  # remember window to show table 

        super().__init__(wg)

        self.setGeometry(10, 10, 280, 500)

        self.setColumnCount(3)

        self.verticalHeader().hide();

        self.updt() # обновить таблицу

        self.setEditTriggers(QTableWidget.NoEditTriggers)  

        self.cellClicked.connect(self.cellClick)  # catch mouse click on table 

    def updt(self):

        self.clear()

        self.setRowCount(0);

        self.setHorizontalHeaderLabels(['id', 'Name', 'Score']) 

        self.wg.cur.execute("select * from students order by id")

        rows = self.wg.cur.fetchall()

        i = 0

        for elem in rows:

            self.setRowCount(self.rowCount() + 1)

            j = 0

            for t in elem: # fill in the line

                self.setItem(i, j, QTableWidgetItem(str(t).strip()))

                j += 1

            i += 1

        self.resizeColumnsToContents()

# Reaction on mouse click inside table 

    def cellClick(self, row, col): # row - row number, col - column number 

        self.wg.idp.setText(self.item(row, 0).text())

        self.wg.fio.setText(self.item(row, 1).text().strip())

        self.wg.oce.setText(self.item(row, 2).text().strip())

app = QApplication(sys.argv)

ex = MyWidget()

ex.show()

sys.exit(app.exec_())




Initial setup on Server F34 already running mariadb-server to 
          run PyQT5 python script via CLI:-

$ mkdir WORK
$ cd WORK
$ sudo dnf install mariadb-connector-c-devel
$ sudo dnf install python-devel
$ sudo dnf install qt5-qtbase-devel
$ sudo dnf install qt5-designer
$ sudo dnf groupinstall "C Development Tools and Libraries"
$ python3 -m venv .envs
$ source .envs/bin/activate
(.envs) [boris@fedora34server WORK] pip3 install mariadb
(.envs) [boris@fedora34server WORK] pip install PyQt5
(.envs) [boris@fedora34server WORK] python  MyProgQT5MDB.py



Setup for MySQL 8.0.25

$ python3 -m venv .env
$ source .env/bin/activate
$ pip install mysql-connector-python
$ ~boris/PYMSQL/.env/bin/python3 -m pip install -- upgrade pip
$ pip install PyQt5
$ python MyProgQT5MDB.py
 
 Code updates
 
  import mysql.connector
  . . . . 
 
   
# connect to datatabase
   def con(self):
       self.conn = mysql.connector.connect(user = "root",
                             password = "*********",
                             host = "127.0.0.1",
                             port = 3306,
                             database = "db1")
       self.cur = self.conn.cursor()
 
Database setup
 
[boris@sever33fedora PYMSQL]$ mysql -u root -p
Enter password:  
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 14
Server version: 8.0.25 MySQL Community Server - GPL

mysql> CREATE DATABASE db1 CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
mysql > use db1;
mysql> CREATE TABLE students (
>     id INT NOT NULL AUTO_INCREMENT,
>     name VARCHAR(30) CHARSET utf8,
>     ocenka INT,
>     PRIMARY KEY (id)
 > );

Saturday, May 29, 2021

Refactoring PyQT5 code of OldProgramer for Complete CRUD App on PostgreSQL 13.2 (F34 Server)

 That is an immediate follow up for post Setup PostgreSQL 13.2 and PyQT5 to run CRUD App in PyCharm 2021.1.1 on F34

Method updating table students has been added to original code and front-end window has been updated as well to have one more button to invoke "Update table" on the fly 

#!/usr/bin/python3

import sys

import psycopg2

from PyQt5.QtWidgets import QTableWidget, QApplication, QMainWindow, QTableWidget 

from PyQt5.QtWidgets import QTableWidgetItem, QWidget, QPushButton, QLineEdit

from PyQt5 import QtGui

class MyWidget(QWidget):

    def __init__(self):

        super().__init__()

        self.con()

        self.setGeometry(100, 100, 500, 600)

        self.setWindowTitle('Scores List')

        self.tb = Tb(self)

        self.btn = QPushButton('Refresh View', self)

        self.btn.resize(150, 40)

        self.btn.move(300, 10)

        self.btn.clicked.connect(self.upd)

        self.idp = QLineEdit(self)

        self.idp.resize(150, 40)

        self.idp.move(300, 60)

        self.idp.setReadOnly(True)

        self.fio = QLineEdit(self)

        self.fio.resize(150, 40)

        self.fio.move(300, 110)

        self.oce = QLineEdit(self)

        self.oce.resize(150, 40)

        self.oce.move(300, 160)

        self.btn = QPushButton('Add row', self)

        self.btn.resize(150, 40)

        self.btn.move(300, 210)

        self.btn.clicked.connect(self.ins)

# update button added by B.D. 

        self.btn = QPushButton('Update row', self)

        self.btn.resize(150, 40)

        self.btn.move(300, 260)

        self.btn.clicked.connect(self.change)

# delete button moved by B.D. 

        self.btn = QPushButton('Delete row', self)

        self.btn.resize(150, 40)

        self.btn.move(300, 310)

        self.btn.clicked.connect(self.dels)

# connect to datatabase 

    def con(self):

        self.conn = psycopg2.connect(user = "postgres",

                              password = "*******",

                              host = "192.168.0.14",

                              port = "5432",

                              database = "db1")

        self.cur = self.conn.cursor()

    def upd(self):

        self.conn.commit()

        self.tb.updt()

        self.idp.setText('')

        self.fio.setText('')

        self.oce.setText('')

    def ins(self):

        fio, oce = self.fio.text(), self.oce.text()

        try:

            self.cur.execute("insert into students (name, ocenka) values (%s,%s)",(fio,oce))

        except:

            pass

        self.upd()

# row update added by B.D. 

    def change(self):

        fio, oce = self.fio.text(), self.oce.text()

        try:

            ids = int(self.idp.text()) 

        except:

            return

        self.cur.execute("update students set name=%s,ocenka=%s where id=%s",(fio,oce,ids,))

        self.upd()

# delete record from table 

    def dels(self):

        try:

            ids = int(self.idp.text()) # line identifier

        except:

            return

        self.cur.execute("delete from students where id=%s",(ids,))

        self.upd()

class Tb(QTableWidget):

    def __init__(self, wg):

        self.wg = wg  # remember window to show table 

        super().__init__(wg)

        self.setGeometry(10, 10, 280, 500)

        self.setColumnCount(3)

        self.verticalHeader().hide();

        self.updt() # обновить таблицу

        self.setEditTriggers(QTableWidget.NoEditTriggers)  

        self.cellClicked.connect(self.cellClick)  # catch mouse click on table 

    def updt(self):

        self.clear()

        self.setRowCount(0);

        self.setHorizontalHeaderLabels(['id', 'Name', 'Score']) 

        self.wg.cur.execute("select * from students order by id")

        rows = self.wg.cur.fetchall()

        i = 0

        for elem in rows:

            self.setRowCount(self.rowCount() + 1)

            j = 0

            for t in elem: # fill inside the line

                self.setItem(i, j, QTableWidgetItem(str(t).strip()))

                j += 1

            i += 1

        self.resizeColumnsToContents()

# Reaction on mouse click inside table 

    def cellClick(self, row, col): # row - row number, col - column number 

        self.wg.idp.setText(self.item(row, 0).text())

        self.wg.fio.setText(self.item(row, 1).text().strip())

        self.wg.oce.setText(self.item(row, 2).text().strip())

app = QApplication(sys.argv)

ex = MyWidget()

ex.show()

sys.exit(app.exec_())


















































Monday, May 24, 2021

Setup PostgreSQL 13.2 and PyQT5 to run CRUD App in PyCharm 2021.1.1 on F34

Running PyQT5 CRUD (PostgreSQL 13.2 ) Application you might experience problem with importing psycopg2 . Posting below briefly provides the way to solve the issue and also describes  in details database setup which is tuned specifically for Fedora 34 .

 Setting up PostgreSQL 13.2 on Fedora 34 Server

sudo dnf module -y install postgresql:13/server

sudo postgresql-setup --initdb

sudo systemctl enable --now postgresql
================================================
This configuration is appropriate only for testing .
================================================
Edit  /var/lib/pgsql/data/pg_hba.conf and replace in field auth-method  "ident" by "md5"

# IPv4 local connections:

host  all   all 127.0.0.1/32  md5

host  all   all 192.168.0.XX/32  md5

# IPv6 local connections:

host  all  all ::1/128 md5

Edit /var/lib/pgsql/data/postgresql.conf

listen_addresses = '*'

$ sudo systemctl restart postgresql.service

$ sudo -u postgres psql

[sudo] password for boris: 

psql (13.2)

Type "help" for help.

postgres=# create database db1 encoding 'UTF8';

CREATE DATABASE

postgres=# grant all privileges on database db1 to postgres;

GRANT

postgres=# alter user postgres  with encrypted password '*******';

postgres=# SELECT datname FROM pg_database;

  datname  

-----------

 postgres

 template1

 template0

 db1

(4 rows)

postgres=# \c db1

db1=# create table students  ( id SERIAL PRIMARY KEY, name VARCHAR,ocenka INT);

PyCharm requires psycopg2's install in particular project VENV , to install issue in the console of your project (ALT+F12) :

$ pip install psycopg2-binary

Setup PyQT5 in PyCharm per http://lxer.com/module/newswire/view/298983/index.html

Imports in Python module should look like :

import sys

import psycopg2

from PyQt5.QtWidgets import QTableWidget, QApplication, QMainWindow, QTableWidget

from PyQt5.QtWidgets import QTableWidgetItem, QWidget, QPushButton, QLineEdit

from PyQt5 import QtGui

=======================

Connection to PostgreSQL :

===============================

# connect to database

    def con(self):

        self.conn = psycopg2.connect(user = "postgres",

                              password = "*******",

                              host = "192.168.0.XX",

                              port = "5432",

                              database = "db1")

        self.cur = self.conn.cursor()















The template of PyQT5 code below belongs OldProgramer@YandexZen. However, been provided to auditorium as is without details of PostgresSQL setup on particular Linux flavor (tuning pg_hba.conf, postgresql.conf)  it doesn't provide the straight forward ability for testing. Moreover in original version of code names of database and table "students" where the same , what potentially might confuse even experienced DBA.

#!/usr/bin/python3

import sys

import psycopg2

from PyQt5.QtWidgets import QTableWidget, QApplication, QMainWindow, QTableWidget 

from PyQt5.QtWidgets import QTableWidgetItem, QWidget, QPushButton, QLineEdit

from PyQt5 import QtGui

class MyWidget(QWidget):

    def __init__(self):

        super().__init__()

        self.con()

        self.setGeometry(100, 100, 500, 600)

        self.setWindowTitle('Persons List')

        self.tb = Tb(self)

        self.btn = QPushButton('Refresh', self)

        self.btn.resize(150, 40)

        self.btn.move(300, 10)

        self.btn.clicked.connect(self.upd)

        self.idp = QLineEdit(self)

        self.idp.resize(150, 40)

        self.idp.move(300, 60)

        self.idp.setReadOnly(True)

        self.fio = QLineEdit(self)

        self.fio.resize(150, 40)

        self.fio.move(300, 110)

        self.oce = QLineEdit(self)

        self.oce.resize(150, 40)

        self.oce.move(300, 160)

        self.btn = QPushButton('Add Row', self)

        self.btn.resize(150, 40)

        self.btn.move(300, 210)

        self.btn.clicked.connect(self.ins)

        self.btn = QPushButton('Delete Row', self)

        self.btn.resize(150, 40)

        self.btn.move(300, 260)

        self.btn.clicked.connect(self.dels)

    def con(self):

        self.conn = psycopg2.connect(user = "postgres",

                              password = "*********",

                              host = "192.168.0.XX",

                              port = "5432",

                              database = "db1")

        self.cur = self.conn.cursor()

    def upd(self):

        self.conn.commit()

        self.tb.updt()

        self.idp.setText('')

        self.fio.setText('')

        self.oce.setText('')

    def ins(self):

        fio, oce = self.fio.text(), self.oce.text()

        try:

           self.cur.execute("insert into students (name, ocenka) values (%s,%s)",(fio,oce))

        except:

            pass

        self.upd()

    def dels(self):

        try:

            ids = int(self.idp.text()) 

        except:

            return

        self.cur.execute("delete from students where id=%s",(ids,))

        self.upd()

class Tb(QTableWidget):

    def __init__(self, wg):

        self.wg = wg

        super().__init__(wg)

        self.setGeometry(10, 10, 280, 500)

        self.setColumnCount(3)

        self.verticalHeader().hide();

        self.updt() 

        self.setEditTriggers(QTableWidget.NoEditTriggers) 

        self.cellClicked.connect(self.cellClick)  

    def updt(self):

        self.clear()

        self.setRowCount(0);

        self.setHorizontalHeaderLabels(['id', 'Name', 'Score']) 

        self.wg.cur.execute("select * from students order by name")

        rows = self.wg.cur.fetchall()

        i = 0

        for elem in rows:

            self.setRowCount(self.rowCount() + 1)

            j = 0

            for t in elem: 

                self.setItem(i, j, QTableWidgetItem(str(t).strip()))

                j += 1

            i += 1

        self.resizeColumnsToContents()

    def cellClick(self, row, col): 

        self.wg.idp.setText(self.item(row, 0).text())

        self.wg.fio.setText(self.item(row, 1).text().strip())

        self.wg.oce.setText(self.item(row, 2).text().strip())

app = QApplication(sys.argv)

ex = MyWidget()

ex.show()

sys.exit(app.exec_())  

Configuring another project to run PyQT5 with PostgreSQL database requires injection PyQT5 and  psycopg2-binary in it's own venv. Now output has been sorted by "id".






















Wednesday, May 12, 2021

Rotating an image in PyQt5

 Code below slightly differs from [ 1 ] , which in turn references original idea proposed in [ 2 ] .  Code below provides ability to traverse the folders and select desired image.  Code in [ 1 ] requires the complete path to "jpeg" file to be submitted as command line argument.export

To suppress QT5 buggy warning add to .bashrc

export QT_LOGGING_RULES='*.debug=false;qt.qpa.*=false'

#!/usr/bin/python3


from PyQt5.QtGui import QPixmap, QImage, QTransform
from PyQt5.QtWidgets import QApplication, QWidget, QFileDialog
from PyQt5.QtWidgets import QLabel
from PyQt5.QtCore import Qt, QSize

class ImageRotate(QWidget):
    def __init__(self, image_path, parent=None):
        super().__init__(parent)
        self.label = QLabel(self)
        self.image = QImage(image_path)  
        self.angle = 0

        self.label.setAlignment(Qt.AlignCenter)  
        pm = QPixmap(self.image)
        self.resize(pm.width(), pm.height())  
        self.label.setPixmap(pm)
        self.setWindowTitle('PyQt5 Picture Rotation')

    def keyPressEvent(self, event):
        # on any key
        self.angle += 45 if event.key() == Qt.Key_Down else -45
        trs = QTransform().rotate(self.angle)
        self.label.setPixmap(QPixmap(self.image).transformed(trs))
        return super().keyPressEvent(event)


if __name__ == '__main__':
    import sys
    from  PyQt5.QtWidgets import QApplication, QFileDialog

    app = QApplication(sys.argv)
    qf = QFileDialog()
    fn = qf.getOpenFileName(None, 'Select file', '/',
        'All files (*);; Pictures (*.jpg *.png *.gif *.tif)')[0]
    w = ImageRotate(fn)
    w.move(app.desktop().screen().rect().center() - w.rect().center())
    w.show()
    sys.exit(app.exec_())

Sunday, May 9, 2021

Test Day:2021-05-09 Kernel 5.12.2 on Fedora 34

**********************

UPDATE as of 5/23/2021

**********************

The most recent koji build of kernel-5.12.5-300.fc34 installed and works fine on Fedora 34 Server. Actually, all koji builds starting kernel-5.12.2-300.fc34 and currently latest kernel-5.12.5-300.fc34 have been installed and tested as advised by RH on Fedora 34 Server during May of 2021.












END UPDATE

All logs report PASSED for each test done and uploaded  as prompted at instruction page.

 $ sudo dnf update -y

 $ sudo dnf install koji

 $ mkdir UPGRADE

 $ cd UPGRADE

 $ koji download-build --arch=x86_64 kernel-5.12.2-300.fc34

 $ ls *.rpm|grep -v debug >install.sh

 $ vi install.sh

 $ chmod a+x  install.sh

 $ sudo ./install.sh

 $ sudo  reboot

 $ sudo dnf install gcc make git libtirpc python3-fedora \

    keyutils libtirpc-devel policycoreutils-python-utils

 $ git clone https://pagure.io/kernel-tests.git

 $ cd *tests

 $ cp config.example .config

 $ ls -la

 $ sudo semanage boolean -m --on selinuxuser_execheap

 $ sudo ./runtests.sh

Test suite called with default

./default/cachedrop                                              PASS    

. . . . . . . . . .                    

./default/timer-overhead                                     PASS                      

Test suite complete                                              PASS 

$ sudo ./runtests.sh -t performance

Test suite called with performance

. . . . .

22433MB OK

22433MB OK

22433MB OK

Using config in CONFIG.fedora34server.localdomain

Sun May  9 08:37:28 PM MSK 2021

Latency measurements

Sun May  9 08:43:52 PM MSK 2021

Calculating file system latency

Sun May  9 08:43:54 PM MSK 2021

Local networking

Sun May  9 08:47:35 PM MSK 2021

Bandwidth measurements

Sun May  9 08:58:42 PM MSK 2021

Calculating context switch overhead

Sun May  9 08:58:45 PM MSK 2021

./performance/lmbench3                                           PASS    

Test suite complete                                                    PASS    

 $ ll logs

total 28

-rw-r--r--. 1 root root  3756 May  9 20:35 kernel-test-1620581651.log.txt

-rw-r--r--. 1 root root 22120 May  9 20:58 kernel-test-1620581808.log.txt

$ sudo semanage boolean -m --off selinuxuser_execheap