Friday, May 3, 2024

Performing CRUD Operations with PostgreSQL and PyQt6 in Python

 In today's technological landscape, efficient data management is crucial for any application. When it comes to developing desktop applications with Python, combining it with PostgreSQL for data storage and PyQt6 for the user interface can be a powerful combination. In this guide, we'll walk through the process of building a simple CRUD (Create, Read, Update, Delete) application using Python, PostgreSQL, and PyQt6.


Pros and Cons of Using PostgreSQL

Using PostgreSQL as a database management system (DBMS) offers several advantages and disadvantages, depending on the specific requirements and context of the application. Let's delve into the pros and cons:

Pros:

  1. Robustness and Reliability:
    • PostgreSQL is known for its robustness and reliability. It has a proven track record of being highly stable, even under high loads and concurrent access.
  2. Feature-Rich:
    • PostgreSQL offers a wide range of advanced features such as support for JSON and JSONB data types, full-text search capabilities, and geospatial data support. These features make it suitable for various types of applications.
  3. ACID Compliance:
    • PostgreSQL adheres to ACID (Atomicity, Consistency, Isolation, Durability) properties, ensuring data integrity and reliability, which is crucial for transactional applications.
  4. Extensibility:
    • PostgreSQL's architecture allows for the development and integration of custom extensions, enabling users to add new functionality and tailor the database to their specific needs.
  5. Community and Support:
    • PostgreSQL has a large and active community of developers and users who contribute to its ongoing development, provide support, and share knowledge through forums, mailing lists, and conferences.
  6. Cross-Platform Compatibility:
    • PostgreSQL is available on various operating systems, including Linux, macOS, and Windows, making it suitable for deployment in diverse environments.
  7. Scalability:
    • PostgreSQL supports advanced replication and clustering features, allowing it to scale horizontally and vertically to meet the demands of growing applications.

Cons:

  1. Complexity of Administration:
    • Managing and administering PostgreSQL databases may require a certain level of expertise, especially for tasks such as performance tuning, backup and recovery, and security configuration.
  2. Resource Intensive:
    • PostgreSQL may consume significant system resources, especially in scenarios involving large datasets or complex queries. Proper resource management and optimization are essential for optimal performance.
  3. Learning Curve:
    • Compared to some other DBMS solutions, such as MySQL, PostgreSQL may have a steeper learning curve, particularly for users who are new to relational databases or SQL.
  4. Limited Compatibility with Some Tools:
    • While PostgreSQL supports SQL standards, it may have limited compatibility with certain tools and frameworks compared to more widely adopted databases like MySQL or Oracle.
  5. Concurrency and Performance:
    • While PostgreSQL is highly performant, in certain scenarios, especially those with extremely high concurrency requirements, other database solutions might offer better performance out-of-the-box.
  6. Initial Setup Complexity:
    • Setting up a PostgreSQL instance with specific configurations tailored to the application's needs may require more effort compared to some other DBMS solutions.
  7. Commercial Support Costs:
    • While PostgreSQL itself is open source and free to use, organizations may opt for commercial support from third-party vendors, which could incur additional costs.

In summary, PostgreSQL is a powerful and reliable open-source DBMS with a rich feature set, making it suitable for a wide range of applications. However, its adoption may require careful consideration of factors such as administration complexity, performance requirements, and available expertise within the development team.

Setting Up the Environment

Before we dive into coding, ensure you have Python installed on your system. Additionally, you'll need to have PostgreSQL installed and running, along with the psycopg2 library for Python, which enables interaction with PostgreSQL databases.

pip install PyQt6 psycopg2

Creating the Database

Let's start by setting up our PostgreSQL database. We'll create a table named items with columns for id, name, and quantity. Below is the SQL script to create this table:

CREATE TABLE IF NOT EXISTS items ( id SERIAL PRIMARY KEY, name VARCHAR(255), quantity INTEGER );

Building the Application

Now, let's dive into the Python code. We'll use PyQt6 to create the GUI and psycopg2 to interact with the PostgreSQL database. The code is structured into two main classes: Database and MainWindow.

The Database Class

The Database class handles database operations such as creating the table, inserting, updating, and deleting items. It establishes a connection to the PostgreSQL database using psycopg2.

The MainWindow Class

The MainWindow class represents the main window of our application. It contains input fields for name and quantity, buttons for adding, updating, and deleting items, and a table to display the items from the database. This class also handles user interactions and updates the UI accordingly.

Running the Application

To run the application, execute the Python script. It will launch a window where you can perform CRUD operations on the items table in the PostgreSQL database.

python your_script_name.py

Conclusion

In this tutorial, we've covered the basics of building a CRUD application using Python, PostgreSQL, and PyQt6. You can extend this application by adding features such as validation, search functionality, or more complex database interactions. With this foundation, you can create powerful desktop applications tailored to your specific needs. Happy coding!


Here is the source code:


import sys
from PyQt6.QtWidgets import (
    QApplication, QMainWindow, QVBoxLayout, QWidget, QHBoxLayout,
    QPushButton, QLabel, QLineEdit, QMessageBox, QTableWidget, QTableWidgetItem
)
import psycopg2
class Database:
    def __init__(self):
        self.conn = psycopg2.connect(
            dbname='db_user', user='postgres', password='xblaster', host='localhost', port='5432'
        )
        self.cur = self.conn.cursor()

    def create_table(self):
        self.cur.execute('''
            CREATE TABLE IF NOT EXISTS items (
                id SERIAL PRIMARY KEY,
                name VARCHAR(255),
                quantity INTEGER
            )
        ''')
        self.conn.commit()

    def insert_item(self, name, quantity):
        self.cur.execute('''
            INSERT INTO items (name, quantity) VALUES (%s, %s)
        ''', (name, quantity))
        self.conn.commit()

    def get_items(self):
        self.cur.execute('SELECT * FROM items')
        return self.cur.fetchall()

    def update_item(self, id, name, quantity):
        self.cur.execute('''
            UPDATE items SET name = %s, quantity = %s WHERE id = %s
        ''', (name, quantity, id))
        self.conn.commit()

    def delete_item(self, id):
        self.cur.execute('DELETE FROM items WHERE id = %s', (id,))
        self.conn.commit()

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("CRUD Example")
        self.setGeometry(100, 100, 600, 400)

        self.database = Database()
        self.database.create_table()

        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)
        layout = QVBoxLayout()
        self.central_widget.setLayout(layout)

        # Widgets
        self.name_input = QLineEdit()
        self.quantity_input = QLineEdit()
        self.add_button = QPushButton("Add")
        self.table = QTableWidget()
        self.table.setColumnCount(3)
        self.table.setHorizontalHeaderLabels(["ID", "Name", "Quantity"])
        self.update_button = QPushButton("Update")
        self.delete_button = QPushButton("Delete")

        # Connect buttons
        self.add_button.clicked.connect(self.add_item)
        self.update_button.clicked.connect(self.update_item)
        self.delete_button.clicked.connect(self.delete_item)

        # Add widgets to layout
        layout.addWidget(QLabel("Name:"))
        layout.addWidget(self.name_input)
        layout.addWidget(QLabel("Quantity:"))
        layout.addWidget(self.quantity_input)
        layout.addWidget(self.add_button)
        layout.addWidget(self.table)
        layout.addWidget(self.update_button)
        layout.addWidget(self.delete_button)

        # Load data
        self.load_data()

        # Connect itemClicked signal
        self.table.itemClicked.connect(self.on_table_item_clicked)

    def on_table_item_clicked(self, item):
        row = item.row()
        name = self.table.item(row, 1).text()
        quantity = self.table.item(row, 2).text()
        self.name_input.setText(name)
        self.quantity_input.setText(quantity)
        
    def load_data(self):
        self.table.setRowCount(0)
        items = self.database.get_items()
        for row_number, row_data in enumerate(items):
            self.table.insertRow(row_number)
            for column_number, data in enumerate(row_data):
                self.table.setItem(row_number, column_number, QTableWidgetItem(str(data)))

    def add_item(self):
        name = self.name_input.text()
        quantity = self.quantity_input.text()
        if name and quantity:
            self.database.insert_item(name, quantity)
            self.load_data()
            self.name_input.clear()
            self.quantity_input.clear()

    def update_item(self):
        row = self.table.currentRow()
        print(self.table.item(row, 0).text())
        if row != -1:
            id = int(self.table.item(row, 0).text())
            name = self.table.item(row, 1).text()
            quantity = self.table.item(row, 2).text()
            self.database.update_item(id, name, quantity)
            self.load_data()
            self.name_input.clear()
            self.quantity_input.clear()

    def delete_item(self):
        row = self.table.currentRow()
        if row != -1:
            id = int(self.table.item(row, 0).text())
            self.database.delete_item(id)
            self.load_data()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

Monday, April 15, 2024

Uncovering Cybersecurity Insights: Python Script to Retrieve Installed Software on Windows

 In the vast landscape of cybersecurity, understanding the software ecosystem within a system is paramount. Malicious software often hides within the labyrinth of installed applications, making it crucial for cybersecurity professionals to have tools to analyze and assess the software environment comprehensively. In this blog post, we'll explore a Python script designed to retrieve information about installed software on Windows systems and discuss its importance in cybersecurity.


The Python Script

The Python script presented in this post utilizes the winreg module to access the Windows Registry, where information about installed software is stored. By traversing specific registry keys, the script extracts details such as software names and versions. The retrieved data is then presented in a structured manner, making it easy to analyze.

Importance in Cybersecurity

1. Vulnerability Management:

Understanding the software landscape is crucial for effective vulnerability management. Outdated or vulnerable software can serve as potential entry points for cyber threats. By regularly running scripts like the one provided, cybersecurity professionals can identify outdated software versions and prioritize patching efforts accordingly.

2. Threat Detection:

Malicious software often disguises itself as legitimate applications. By maintaining an inventory of installed software and regularly comparing it against known good software lists, security teams can detect anomalies and potential threats. Any unanticipated or unauthorized software installations can raise red flags, triggering further investigation.

3. Incident Response:

During incident response procedures, having comprehensive information about installed software is invaluable. It allows responders to quickly assess the software landscape, identify potentially compromised applications, and take appropriate mitigation actions. Moreover, analyzing software versions can provide insights into the attack vector and help in attributing the incident.

4. Compliance and Auditing:

Many cybersecurity regulations and standards require organizations to maintain an inventory of installed software as part of compliance and auditing processes. By automating the retrieval of software information using scripts, organizations can streamline compliance efforts and ensure adherence to regulatory requirements.

Conclusion

In the ever-evolving landscape of cybersecurity, having tools to gain insights into the software environment is indispensable. The Python script showcased in this post serves as a valuable asset for cybersecurity professionals, enabling them to retrieve essential information about installed software on Windows systems. Whether it's for vulnerability management, threat detection, incident response, or compliance, understanding the software ecosystem lays the foundation for a robust cybersecurity posture.

By leveraging automation and scripting, cybersecurity teams can enhance their capabilities, stay proactive in identifying potential risks, and ultimately bolster the overall security posture of their organizations. It's not just about knowing what software is installed—it's about using that knowledge to fortify defenses and safeguard against emerging cyber threats.


Here is the code:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import sys
from PyQt6.QtWidgets import QApplication, QMainWindow, QTableWidget, QTableWidgetItem, QVBoxLayout, QWidget, QHeaderView
from PyQt6.QtCore import Qt
import winreg

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("Installed Software")
        self.setGeometry(100, 100, 600, 400)

        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)
        self.layout = QVBoxLayout()
        self.central_widget.setLayout(self.layout)

        self.table_widget = QTableWidget()
        self.layout.addWidget(self.table_widget)

        self.populate_table()

    def populate_table(self):
        self.table_widget.setColumnCount(2)
        self.table_widget.setHorizontalHeaderLabels(["Software", "Version"])

        installed_software = self.get_installed_software()
        row_count = len(installed_software)
        self.table_widget.setRowCount(row_count)

        for row, (software, version) in enumerate(installed_software):
            self.table_widget.setItem(row, 0, QTableWidgetItem(software))
            self.table_widget.setItem(row, 1, QTableWidgetItem(version))

        # Set resizing policy and initial column widths
        self.table_widget.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.Fixed)
        self.table_widget.setColumnWidth(0, 250)
        self.table_widget.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)

    def get_installed_software(self):
        software_list = []
        uninstall_key = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
        with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, uninstall_key) as key:
            for i in range(winreg.QueryInfoKey(key)[0]):
                try:
                    subkey_name = winreg.EnumKey(key, i)
                    subkey = winreg.OpenKey(key, subkey_name)
                    display_name = winreg.QueryValueEx(subkey, "DisplayName")[0]
                    version = winreg.QueryValueEx(subkey, "DisplayVersion")[0]
                    software_list.append((display_name, version))
                    winreg.CloseKey(subkey)
                except FileNotFoundError:
                    continue
        return software_list

def main():
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

if __name__ == "__main__":
    main()

Friday, April 5, 2024

Enhancing the Simple File List App with Tab Widget Display(3rd Upgrade)


 In a previous article, we explored the development of a Simple File List App(2nd Upgrade) using PyQt6 and Python. This application provided users with a convenient interface for browsing and inspecting files within a selected directory. However, we recognized the potential for further improvement to enhance usability and functionality. In this follow-up article, we'll delve into how we can elevate the application by introducing a tab widget to display each file individually.



Introduction

The Simple File List App initially presented users with a hierarchical view of files and directories, allowing them to select files for inspection. While this provided a functional interface, there was room for enhancement to offer a more intuitive and versatile user experience. By introducing a tab widget, we can enable users to view each file's contents in a separate tab, akin to a modern text editor or file explorer application.

Components

To implement this enhancement, we'll introduce the following components:

  • QTabWidget: This widget will serve as the container for displaying individual files in separate tabs, providing users with a tabbed interface for easier navigation and organization of file contents.
  • QPushButton: We'll add a button to each tab to allow users to close individual tabs, providing flexibility and control over their workspace.
  • Signal-Slot Connections: We'll connect signals emitted by the file tree view to dynamically create tabs and display file contents when files are selected.

Functionality

With the introduction of the tab widget, the application's functionality will be expanded as follows:

  • Tabbed Interface: Each file selected in the file tree view will be displayed in a separate tab within the tab widget. This allows users to switch between files seamlessly and view multiple files simultaneously.
  • Dynamic Tab Creation: Tabs will be created dynamically as users select files, ensuring an efficient use of screen real estate and providing a clutter-free workspace.
  • Tab Close Button: A close button will be added to each tab, allowing users to close individual tabs when they are no longer needed, enhancing the application's usability and flexibility.

Conclusion

By enhancing the Simple File List App with a tab widget to display files individually, we've elevated its usability and functionality, providing users with a more intuitive and versatile interface for file browsing and inspection. This enhancement not only improves the user experience but also demonstrates the flexibility and power of PyQt6 and Python in developing rich desktop applications. With further iterations and refinements, the application can continue to evolve to meet the needs and preferences of its users, making it a valuable tool for file management and exploration.

Here is the complete code:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import sys
from PyQt6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QFileDialog, QTreeView, QPushButton, QWidget, QTextEdit, QSplitter, QTabWidget
from PyQt6.QtGui import QStandardItemModel, QStandardItem, QFileSystemModel
from PyQt6.QtCore import Qt, QDir, QModelIndex

# Global variable to store the folder path
folder_path = ""

class FileListApp(QMainWindow):
    def __init__(self):
        super().__init__()

        self.initUI()

    def initUI(self):
        self.setWindowTitle('File List App')
        self.setGeometry(100, 100, 800, 600)

        # Create widgets
        self.tree_view = QTreeView()
        self.open_button = QPushButton('Open Folder', self)
        
        self.tab_widget = QTabWidget(self)

        # Create a splitter to separate the tree view, tab widget, and the text edit
        self.splitter = QSplitter(Qt.Orientation.Horizontal)
        self.splitter.addWidget(self.tree_view)
        self.splitter.addWidget(self.tab_widget)
        

        # Create a close button
        self.close_button = QPushButton("X")
        self.close_button.clicked.connect(self.closeActiveTab)

        # Add the close button to the corner of the tab bar
        self.tab_widget.setCornerWidget(self.close_button)

        # Create layout
        layout = QVBoxLayout()
        layout.addWidget(self.open_button)
        layout.addWidget(self.splitter)

        # Create central widget and set layout
        central_widget = QWidget()
        central_widget.setLayout(layout)
        self.setCentralWidget(central_widget)

        # Connect the openFolder button to the openFolder slot
        self.open_button.clicked.connect(self.openFolder)

        # Connect tabCloseRequested signal to closeTab slot
        self.tab_widget.tabCloseRequested.connect(self.closeTab)

    def closeActiveTab(self):
        current_index = self.tab_widget.currentIndex()
        self.tab_widget.removeTab(current_index)

    def openFolder(self):
        global folder_path
        folder_path = QFileDialog.getExistingDirectory(self, 'Open Folder')

        if folder_path:
            self.setWindowTitle(f'File List App - {folder_path}')
            self.displayFolderContent(folder_path)

    def displayFolderContent(self, folder_path):
        # Set up the file system model
        model = QFileSystemModel()
        model.setRootPath(folder_path)
        model.setFilter(QDir.Filter.NoDotAndDotDot | QDir.Filter.AllEntries)

        # Set the model to the tree view
        self.tree_view.setModel(model)
        self.tree_view.setRootIndex(model.index(folder_path))
        self.tree_view.setColumnWidth(0, 250)  # Adjust column width

        # Connect itemClicked signal to displayFileContent
        self.tree_view.clicked.connect(self.displayFileContent)

        # Adjust the sizes of the splitter
        self.splitter.setSizes([int(self.width() * 0.3), int(self.width() * 0.4), int(self.width() * 0.3)])

    def resizeEvent(self, event):
        super().resizeEvent(event)
        # Calculate the width of the button widget
        button_width = self.tab_widget.cornerWidget().sizeHint().width()
        # Update splitter sizes when window is resized
        self.splitter.setSizes([int(self.width() * 0.3), int(self.width() * 0.4) - button_width, int(self.width() * 0.3)])
        
    def displayFileContent(self, index: QModelIndex):
        global folder_path
        # Get the file path from the selected index
        file_path = self.tree_view.model().filePath(index)

        # Check if the file is already open in a tab
        for i in range(self.tab_widget.count()):
            if self.tab_widget.widget(i).objectName() == file_path:
                # Switch to the tab containing the file
                self.tab_widget.setCurrentIndex(i)
                return

        # Read the content of the file using utf-8 encoding
        try:
            with open(file_path, 'r', encoding='utf-8') as file:
                content = file.read()
                # Create a new tab and display file content
                text_edit = QTextEdit()
                text_edit.setReadOnly(True)
                text_edit.setText(content)
                text_edit.setObjectName(file_path)  # Set object name to file path
                self.tab_widget.addTab(text_edit, file_path.split('/')[-1])
        except Exception as e:
            self.text_edit.setText(f"Error reading file: {str(e)}")

    def closeTab(self, index):
        widget = self.tab_widget.widget(index)
        if widget is not None:
            widget.deleteLater()
            self.tab_widget.removeTab(index)

def main():
    app = QApplication(sys.argv)
    window = FileListApp()
    window.show()
    sys.exit(app.exec())

if __name__ == '__main__':
    main()

Monday, April 1, 2024

Exploring Common Vulnerabilities and Exposures (CVEs) in Python and Its Ecosystem: A Comprehensive Overview

 To find Common Vulnerabilities and Exposures (CVE) related to Python and its libraries, you can use various vulnerability databases and security advisories. Here are some resources you can check:

  1. National Vulnerability Database (NVD): The NVD provides a comprehensive database of vulnerabilities. You can search for vulnerabilities related to Python and its libraries using keywords or specific product names.
  2. CVE Details: CVE Details is a website that provides information about vulnerabilities, including those related to Python and its libraries. You can search for CVEs using keywords or product names.
  3. Security Advisories: Check security advisories from Python itself and from popular libraries. For example, Python has a security page where you can find security-related announcements and advisories. Many popular libraries also have security pages or mailing lists where they announce vulnerabilities and fixes.
  4. GitHub Security Advisories: GitHub provides security advisories for repositories. You can search for security advisories related to Python libraries on GitHub.
  5. Vulnerability Scanners: Consider using vulnerability scanners or security tools that can scan your Python dependencies for known vulnerabilities. These tools can help identify vulnerable dependencies in your projects.

When searching for vulnerabilities, make sure to use relevant keywords such as "Python", "pip", or specific library names to narrow down the search results. Additionally, always keep your Python installations and dependencies up to date to mitigate the risk of known vulnerabilities.