Python Graphical User Interface

Insights of Item Based Widgets in QT Designer

A.I Hub
14 min readSep 21, 2024
Image By Author

When it comes to crafting dynamic and organized user interfaces in PyQt5, widgets are your ultimate toolkit. The List Widget allows for seamless presentation and interaction with lists of items, making it essential for everything from to-do lists to file explorers. The Tree Widget elevates your app’s functionality, offering hierarchical data visualization that is perfect for displaying parent child relationships, like file directories or organizational charts and for those seeking structure and precision, the Table Widget is indispensable for managing and displaying tabular data, offering flexibility and control over multi-column layouts. Each of these widgets brings unique power to your application, transforming basic interfaces into robust, interactive environments that enhance user experience.

Table of Content

  • Introduction
  • List widget
  • Tree widget
  • Table widget

Introduction

In the previous sections, we discussed different item views in

PyQt5. In this section, we will focus on Item Widgets. Our

primary focus in this section will be on learning about List

Widget, Tree Widget and Table Widget. We will be focusing on

properties and some useful methods. All these widgets are

available under Item Widgets in Qt Designer, as shown in the figure 1.1, where the user can select any of the
widgets under Item Widgets and drag and drop in a GUI form.

Figure 1.1 - Different Item Widgets (Item Based) in Qt Designer

We have already seen about QObject and QWidget classes when
we were dealing with button type widgets. The same applies

to Item Widgets too. We have already discussed other classes

about QFrame, QAbstractScrollArea and QAbstractItemView class in

the previous section. The Item Widgets have all these range of

properties. But the default values may differ from widget to

widget. Now, we shall be discussing 3 item widgets and their
properties.

List Widget

List widget is a classic item based interface for adding and
removing items. It has a list view similar to the one supplied
by QListView, provided by the convenience class QListWidget. An
item based interface to add or delete items from a list is the
QListWidget class. The list’s items are all QListWidgetItem objects.

Each QListWidgetItem in the list is managed by QListWidget using

an internal model. The multi selectability feature can be set
for the QListWidget object.

Important Properties

We have already seen the different QListView properties in the

previous chapter. The same properties apply here too. Apart

from these properties, we shall discuss QListWidget properties.

Current row

The current item’s row is set using this property. The row
may also be selected depending on the active selection
mode. Default value is -1.

Figure 1.2 - Image depicting currentRow property of QListWidget in Qt
Designer

Sorting enabled

The default value is unchecked and when set to true, this
property will decide that sorting is enabled for the list.

Figure 1.3 - Image depicting sortingEnabled property of QListWidget in Qt
Designer

Important Methods

  • clear(): All the items from the QListWidget object will be

    removed as it will be permanently deleted.
  • insertItem(row, item): The QListWidgetItem object will be

    inserted at the position specified by the row (integer).
  • addItem(): Either a QListWidgetItem object or string will be
    added to the QListWidget object.
  • addItems(labels): Items with a list of strings will be
    inserted at the end of the QListWidget object.
  • setCurrentItem(item, command): Programmatically, we can
    set the currently selected item to the QListWidgetItem
    object with/without the usage of selection flags, that is,
    using the command parameter.
  • sortItems([order=Qt.AscendingOrder]): QListWidget items will

    be arranged according to the specified order.

Important Signals

  • currentItemChanged(): The signal is emitted whenever the

    current item changes of the QListWidget object.
  • itemClicked(): The signal is emitted whenever an item in

    the QListWidget object is clicked.

Now, we shall see an example of QListWidget.

Details of file names
Figure 1.4 - Qt Designer file: ListWidget/listWidget_eg1.ui

Consider the following code of run_listWidget_eg1.py:

import sys
from PyQt5.QtWidgets import
QMainWindow,QAbstractItemView,
QApplication,QListWidgetItem,QCheckBox,QHBoxLayout,
QLabel,QWidget, QMessageBox
from PyQt5.QtGui import QPalette, QColor
from listWidget_eg1 import *
class MyListWidget(QMainWindow):
def __init__(self):
super().__init__()
self.myui = Ui_MainWindow()
self.myui.setupUi(self)
self.myui.lw_Add.clicked.connect(self.btn1)

self.myui.lw_Count.clicked.connect(self.btn2)

self.myui.lw_Clear.clicked.connect(self.btn3)

self.myui.lw_SortAsc.clicked.connect(self.btn4)

self.myui.lw_Toggle.clicked.connect(self.btn5)

self.myui.lw_SetCurrentItem.clicked.connect(self.btn6)

self.myui.lw_AddCheckBox.clicked.connect(self.btn7)
self.myui.lw1.setDragEnabled(True) #
allowing dragenabled property of lw1 to be True
self.myui.lw2.setDragEnabled(False)#
allowing dragenabled property of lw2 to be False

self.myui.lw1.setDragDropMode(QAbstractItemView.DragDrop) # setting dag drop mode of lw1
self.myui.lw1.setAcceptDrops(False)#
allowing acceptDrops property of lw1 to be False
self.myui.lw2.setAcceptDrops(True)#
allowing acceptDrops property of lw2 to be True
self.show()

# Add items to the list widget
for loop in range(5):
item = QListWidgetItem()
item.setText(" Itemmno:
{}".format(loop+1))
self.myui.lw1.addItem(item)
def btn1(self):
# creating an instance of QListWidgetItem
item = QListWidgetItem()
# setting the text
item.setText(" Itemmno:
{}".format(self.myui.lw1.count()+1))
# adding it to the listWidget using addItem
method
self.myui.lw1.addItem(item)

# Add multiple items to the list widget
mylist_items = [" Zeeshan", " Vicky",
" Abdul"]
for list_item in mylist_items:
myinst_item = QListWidgetItem()
myinst_item.setText(list_item)
self.myui.lw1.addItem(myinst_item)

def btn2(self):
QMessageBox.information(None, 'Item Count
Title', "The number of items in the list is
{}".format(self.myui.lw1.count()))

def btn3(self):
# clearing all the listWidget items
self.myui.lw1.clear()

def btn4(self):
# Sorting the listWidget items in ascending order
self.myui.lw1.sortItems(QtCore.Qt.AscendingOrder)

def btn5(self):
# Enable alternate row colors
self.myui.lw1.setAlternatingRowColors(True)
mypalette = self.myui.lw1.palette()
mypalette.setColor(QPalette.AlternateBase,
QColor("lightgray"))
self.myui.lw1.setPalette(mypalette)

def btn6(self):
myselect_item = self.myui.lw1.item(4) #
Selecting 5th item of the list
self.myui.lw1.setCurrentItem(myselect_item)
# will set the current item of the listWidget

# Emitting the itemSelectionChanged signal
self.myui.lw1.itemSelectionChanged.emit() #
signal will be emitted when the selection of items
in the list widget is changed

def btn7(self):
#Create a checkbox for each ListWidget item
for myitm in range(self.myui.lw1.count()):
myitem = self.myui.lw1.item(myitm)
mycheck_box = QCheckBox()
self.myui.lw1.setItemWidget(myitem, mycheck_box)


if __name__=="__main__":
app = QApplication(sys.argv)
w = MyListWidget()
w.show()
sys.exit(app.exec_())
Figure 1.5 - Default output of ListWidget/run_ listWidget_eg1.py
Figure 1.6 - Case 1 Output of ListWidget/run_ listWidget_eg1.py

Case 2 — On pressing Count button

Figure 1.7 - Case-2 Output of ListWidget/run_ listWidget_eg1.py

Case 3 — On pressing Sort Ascending Order button

Figure 1.8 - Case-3 Output of ListWidget/run_ listWidget_eg1.py

Case 4 — On pressing Toggle Alternating RowColors button

Figure 1.9 - Case-4 Output of ListWidget/run_ listWidget_eg1.py

Case 5 — On pressing the Set Current item and Emit Item Selection Changed Signal button

Figure 1.10 - Case-5 Output of ListWidget/run_ listWidget_eg1.py

Case 6 — On pressing the Adding checkbox to each Item of List Widget1 button

Figure 1.11 - Case-6 Output of ListWidget/run_ listWidget_eg1.py

Case 7 — On pressing Clear button

Figure 1.12 - Case-7 Output of ListWidget/run_ listWidget_eg1.py

Case 8 — Drag and Drop from lw1 to lw2 only

Figure 1.13 - Case-8 Output of ListWidget/run_ listWidget_eg1.py

Tree Widget

The PyQt5 library’s QTreeWidget class offers a treeView widget

for displaying data in a hierarchical structure. It supports

multiple columns and custom items and the user can
expand and collapse branches to view and edit items. Data
can be displayed using it, including a file system, database or list of items with parent-child relationships. The QTreeWidget
class utilizes a default model to hold items, each of which is
a QTreeWidgetItem, and is based on Qt’s Model/View

architecture.

Important Properties

The properties of QTreeWidget are similar to the QTreeView

properties seen in the previous section. Apart from these

properties, the only other property which is added is
columnCount property. This property will return the number of
columns count in the QTreeWidget. Default value is set to 1. We
can also set the column count by using the

setColumnCount(count) method.

Figure 1.14 - Image depicting columnCount property of QTreeWidget in Qt
Designer
  • clear(): All the items from the QTreeWidget object will be

    removed as it will be permanently deleted.
  • addTopLevelItem(item): A top-level item can be added to

    the QTreeWidget object using this method. The
    QTreeWidgetItem or one of its subclass can be the item

    parameter.
  • currentItem(): The current item in the QTreeWidget object

    (the one that is selected and has focus) is returned by

    this method.
  • selectedItems(): This method will return a list of all

    selected non-hidden items in the QTreeWidget object.
  • setHeaderLabels(labels): This method is used to set the

    header labels list of strings for each column of the
    QTreeWidget object.
  • sortItems(column,order): This function uses the specified

    column and orders to sort the items in the QTreeWidget object. The order parameter is a Qt.SortOrderenum
    Qt.AscendingOrder or Qt.DescendingOrder and the column
    parameter is an integer that specifies the column to

    sort by.
  • takeTopLevelItem(index): This method is used to remove an

    item from the QTreeWidget object at the specified index

    parameter and return it. Otherwise, it will return None.

    The index parameter is the index of the item to be

    removed.
  • setColumnCount(columns): The number of columns displayed.

Important Signals

  • itemActivated(item,column): When an item is activated

    double-clicked or pressed when the Enter key is
    pressed, this signal is emitted. The item parameter is
    the activated item and the column parameter is the
    column of the item.
  • itemChanged(item,column): When an item’s data has been

    changed, this signal is emitted. The item parameter is
    the changed item and the column parameter is the
    column of the item.
  • itemClicked(item,column): When an item is clicked, this

    signal is emitted. The item parameter is the clicked
    item and the column parameter is the column of the
    item.
  • itemCollapsed(item): When an item is collapsed its
    children are hidden, this signal is emitted. The item
    parameter is the collapsed item.
  • itemDoubleClicked(item,column): When an item is double

    clicked, this signal is emitted. The item parameter is the double clicked item and the column parameter is
    the column of the item.
  • itemEntered(item,column): When the mouse-cursor enters

    an item, this signal is emitted. The item parameter is
    the entered item and the column parameter is the
    column of the item.
  • itemExpanded(item): This signal is emitted when an item is
    expanded its children are shown. The item parameter

    is the expanded item.
  • itemPressed(item,column): When an item is pressed, this

    signal is emitted. The item parameter is the pressed

    item and the column parameter is the column of the
    item.

Now, we shall see an example of QTreeWidget.

Details of file names
Figure 1.15 - Qt Designer file: TreeWidget/ treeWidget_eg1.ui

Consider the following code of run_treeWidget_eg1.py:

import sys
from PyQt5.QtWidgets import QMainWindow,
QApplication, QTreeWidget, QTreeWidgetItem
from PyQt5.QtGui import QPalette, QColor
from treeWidget_eg1 import *

class MyTreeWidget(QMainWindow):
def __init__(self):
super().__init__()
self.myui = Ui_MainWindow()
self.myui.setupUi(self)

# Initially setting the column count of the
treeWidget object to 2
self.myui.treeWidget.setColumnCount(2)
# Set the header labels for 2 columns

self.myui.treeWidget.setHeaderLabels(["Name","Age"])
# Adding items to the treeWidget object
myitem1 = QTreeWidgetItem(["Saurabh", "34"])
myitem2 = QTreeWidgetItem(["Nilesh", "42"])
myitem3 = QTreeWidgetItem(["Priyanka", "30"])
myitem4 = QTreeWidgetItem(["Pranav", "31"])
myitem5 = QTreeWidgetItem(["Papa", "61"])
myitem6 = QTreeWidgetItem(["Mummy", "60"])

# For TreeWidget1

self.myui.treeWidget.addTopLevelItem(myitem1)

self.myui.treeWidget.addTopLevelItem(myitem2)
self.myui.treeWidget.addTopLevelItem(myitem3)

self.myui.treeWidget.addTopLevelItem(myitem4)

self.myui.treeWidget.addTopLevelItem(myitem5)

self.myui.treeWidget.addTopLevelItem(myitem6)

# Connecting the itemClicked signal to the
handle_item_click method

self.myui.treeWidget.itemClicked.connect(self.myhan
dle_item_click)
# Connecting the itemDoubleClicked signal
to the handle_item_double_click method

self.myui.treeWidget.itemDoubleClicked.connect(self
.myhandle_item_double_click)
# Connecting the itemActivated signal to
the handle_item_activation method

self.myui.treeWidget.itemActivated.connect(self.myh
andle_item_activation)

# For TreeWidget2
# Setting the column count to 1
self.myui.treeWidget_2.setColumnCount(1)
# Create the top-level items
self.myitemA =
QTreeWidgetItem(self.myui.treeWidget_2, ["Item A"])
self.myitemB =
QTreeWidgetItem(self.myui.treeWidget_2, ["Item B"])
self.myitemC =
QTreeWidgetItem(self.myui.treeWidget_2, ["Item C"])
# Create the child items for Item A
self.myitemA1 =
QTreeWidgetItem(self.myitemA, ["Item A_1"])
self.myitemA2 =
QTreeWidgetItem(self.myitemA, ["Item A_2"])
# Create the child items for Item B
self.myitemB1 =
QTreeWidgetItem(self.myitemB, ["Item B_1"])
self.myitemB2 =
QTreeWidgetItem(self.myitemB, ["Item B_2"])
# Create the child items for Item C
self.myitemC1 =
QTreeWidgetItem(self.myitemC, ["Item C_1"])

self.myitemC2 =
QTreeWidgetItem(self.myitemC, ["Item C_2"])
# Connect the itemExpanded and
itemCollapsed signals to their respective slots

self.myui.treeWidget_2.itemExpanded.connect(self.my
_on_item_expanded)

self.myui.treeWidget_2.itemCollapsed.connect(self.m
y_on_item_collapsed)
self.show()

# Method to handle item click event
def myhandle_item_click(self,item, column):
print("An Item is clicked:",
item.text(column))
# Method to handle item double click event
def myhandle_item_double_click(self,item,
column):
print("An Item is double clicked:",
item.text(column))
# Method to handle item activation event
def myhandle_item_activation(self,item, column):
print("An Item is activated:",
item.text(column))

def my_on_item_expanded(self, item):
# Performing some action when an item is expanded
print(f"{item.text(0)} was expanded")
def my_on_item_collapsed(self, item):
# Performing some action when an item is collapsed
print(f"{item.text(0)} was collapsed")
if __name__=="__main__":
app = QApplication(sys.argv)
w = MyTreeWidget()
w.show()
sys.exit(app.exec_())

Output:

Figure 1.16 - Default Case output of TreeWidget/ run_treeWidget_eg1.py

Case 1 — Output display when performing Item clicked
event on Name = Saurabh

Figure 1.17 - Case 1 output of TreeWidget/ run_treeWidget_eg1.py

Case 2 — Output display when performing Item double-

clicked event on Name = Nilesh

Figure 1.18 - Case-2 output of TreeWidget/ run_treeWidget_eg1.py

Here, on pressing double-clicked event on Name = Nilesh, the

first clicked event is triggered followed by double clicked
event and then the activated event.

Case 3 — Output display when performing Item
Activated event on Name = Priyanka

Figure 1.19 - Case-3 output of TreeWidget/ run_treeWidget_eg1.py

Here , Enter key is pressed when the control is on Name =
Priyanka. That is why the initially clicked event is triggered
along with the activated event on pressing Enter key.

Case 4 — Output display when performing Item Expanded event

Figure 1.20 - Case-4 output of TreeWidget/ run_treeWidget_eg1.py

Here, Item A, Item B and Item C was expanded triggering
expanded event.

Case 5 — Output display when performing Item Collapsed event

Figure 1.21 - Output of TreeWidget/ run_treeWidget_eg1.py

Here, Item A, Item B and Item C was collapsed triggering
collapsed event.

Table Widget

A widget called QTableWidget in PyQt5 displays data in a table

format with rows and columns. It supports several types of
data like text, icons, and checkboxes and allows for data
manipulation such as sorting and editing. Additionally, it can
be used to display information from a model or database. It is comparable to QTableView but offers more efficient data handling techniques. QTableWidgetItem provides the items for a
QTableWidget.

Important Properties

The properties of QTableWidget are similar to the QTableView

properties seen in the previous chapter. Apart from these properties, the only other 2 properties which are added are

as follows.

rowCount

This property will return the number of rows in the

QTableWidget object. We can set the number of rows by passing

an integer value to this property. The default value is set to
0.

Figure 1.22 - Image depicting rowCount property of QTableWidget in Qt
Designer

Column count

This property will return the number of columns in the

QTableWidget object. We can set the number of columns by
passing an integer value to this property. The default value is
set to 0.

Figure 1.23 - Image depicting columnCount property of QTableWidget in Qt
Designer

Important Methods

  • setItem(int row, int column, QTableWidgetItem): This method
    will set the item at a specified row and column position
    in the QTableWidget object. The item may be an object of
    the class QTableWidgetItem or one of its subclasses.
  • item(int row, int column): The item is returned as a

    QTableWidget object at a specified row and column

    position if set otherwise returns None.
  • setCellWidget(int row, int column, QWidget): This method
    sets a widget that is to be displayed in the cell at a

    specified row and column position in the QTableWidget

    object. This can be useful to display data types such as

    a push button or a checkbox.
  • cellWidget(int row, int column): This method returns a
    widget as a QWidget object at a specified row and column

    position in the QTableWidgetItem object.
  • removeRow(int row): This method will remove a row from

    the QTableWidget object where the row number is passed

    as an argument.
  • removeColumn(int column): This method will remove a

    column from the QTableWidget object where the column

    number is passed as an argument.
  • sortItems(int column[,order=Qt.AscendingOrder]): This method

    sorts the rows in the QTableWidget object based on the
    values in a specified column where the column number

    and the sort order ascending or descending are
    passed as arguments.
  • clear(): Using this method, all the items and widgets
    are removed from the QTableWidget object.

Important Signals

  • itemSelectionChanged(): The signal is emitted when the

    selection in the QTableWidget object is changed.
  • cellClicked(row,column): The signal is emitted when the

    cell in the QTableWidget object is clicked and takes the
    row and column of the clicked cell as arguments.
  • cellDoubleClicked(row,column): The signal is emitted when

    the cell in the QTableWidget object is double-clicked and

    takes the row and column of the double-clicked cell as

    arguments.
  • cellChanged(row,column): The signal is emitted when the

    cell in the QTableWidget object is changed and takes the

    row and column of the changed cell as arguments.
  • cellEntered(row,column): The signal is emitted when the

    user enters a cell in the QTableWidget object and takes the
    row and column of the entered cell as arguments. The

    cellEntered signal is only triggered when the QTableWidget
    object has the focus, which occurs when the user clicks
    inside or tabs to the object.
  • cellPressed(row,column): The signal is emitted when the

    user presses a cell in the QTableWidget object and takes

    the row and column as arguments.

Now, we shall see an example of QTableWidget.

Details of file names
Figure 1.24 - Qt Designer file: TableWidget/tableWidget_eg1..ui

Consider the following code of run_tableWidget_eg1.py:

import sys
from PyQt5.QtWidgets import QMainWindow,
QApplication,QTableWidgetItem,QHeaderView
from PyQt5.QtGui import QPalette, QColor
from tableWidget_eg1 import *

class MyTableWidget(QMainWindow):
def __init__(self):
super().__init__()
self.myui = Ui_MainWindow()
self.myui.setupUi(self)

# Seting the number of rows and columns of
QTableWidget object
self.myui.tableWidget.setRowCount(2)
self.myui.tableWidget.setColumnCount(3)
# Set the horizontal headers for the table

self.myui.tableWidget.setHorizontalHeaderLabels(["N
ame", "Age", "Sex"])
# Adding some data to the QTableWidget object

self.myui.tableWidget.setItem(0,0,QTableWidgetItem("Saurabh"))

self.myui.tableWidget.setItem(0,1,QTableWidgetItem("34"))

self.myui.tableWidget.setItem(0,2,QTableWidgetItem("Male"))
self.myui.tableWidget.setItem(1,0,QTableWidgetItem("Aditi"))

self.myui.tableWidget.setItem(1,1,QTableWidgetItem("30"))

self.myui.tableWidget.setItem(1,2,QTableWidgetItem("Female"))

#for fitting the QTableWidget object horizontally

self.myui.tableWidget.horizontalHeader().setStretch
LastSection(True)

self.myui.tableWidget.horizontalHeader().setSection
ResizeMode(QHeaderView.Stretch)

# connect signals to slots

self.myui.tableWidget.cellChanged.connect(self.myce
llChanged)

self.myui.tableWidget.cellClicked.connect(self.myce
llClicked)

self.myui.tableWidget.cellEntered.connect(self.mycellEntered)
self.myui.tableWidget.cellPressed.connect(self.myce
llPressed)

# clicked signal of button connecting to slot btn_click

self.myui.btn_Add.clicked.connect(self.btn_click)

self.show()

# defining the slots
def mycellChanged(self, myrow, mycol):
print("Cell is changed at row {0}, col
{1}".format(myrow, mycol))
def mycellClicked(self, myrow, mycol):
print("Cell is clicked at row {0}, col
{1}".format(myrow, mycol))
def mycellEntered(self, myrow, mycol):
print("Cell is entered at row {0}, col
{1}".format(myrow, mycol))
def mycellPressed(self, myrow, mycol):
print("Cell is pressed at row {0}, col
{1}".format(myrow, mycol))
def btn_click(self):
# inserting a new row at the end of the
QTableWidget object by taking rowCount
myrow = self.myui.tableWidget.rowCount()
self.myui.tableWidget.insertRow(myrow)
# adding fixed name, age and sex to display
to the user of inserting new data
self.myui.tableWidget.setItem(myrow, 0,
QTableWidgetItem("Divya"))
self.myui.tableWidget.setItem(myrow, 1,
QTableWidgetItem("36"))
self.myui.tableWidget.setItem(myrow, 2,
QTableWidgetItem("Male"))
if __name__=="__main__":
app = QApplication(sys.argv)
w = MyTableWidget()
w.show()
sys.exit(app.exec_())

Output:

Figure 1.25 - Default case output of TableWidget/ run_tableWidget_eg1.py

Case 1 — When Add button is clicked on QTableWidget object

Figure 1.26 - Case 1 output of TableWidget/ run_tableWidget_eg1.py

When new data is added in the row of the QTableWidget object then the cellChanged event is triggered.

Case 2 — When a cell is clicked or pressed on the QTableWidget object

Figure 1.27 - Case-2 output of TableWidget/ run_tableWidget_eg1.py

When a cell with data row number as 2 and column number
as 0 is clicked, then first cellPressed and then the cellClicked
event is triggered.

Case 3 — When a cell is changed on the QTableWidget object

Figure 1.28 - Case-3 output of TableWidget/ run_tableWidget_eg1.py

When we are trying to change the data of row number 2 and

column number 0 and after changing when pressing Enter

button, the cellChanged event is triggered.

Case 4 — When a cell is entered on the QTableWidget object

Figure 1.29 - Output of TableWidget/ run_tableWidget_eg1.py

When the user enters a cell with row number 2 and columns
0 and 1 then the cellEntered event is triggered.

Conclusion

In this section, we will learned about in-depth understanding of

Item Widgets, including QListWidget, QTreeWidget and QTableWidget
which allows for creation of dynamic user interfaces. This
section has provided a comprehensive understanding of Item Widgets in Qt Designer which are essential for creating
interactive user interfaces. By delving into their properties,
functionality, customization options and operational
techniques within the Qt Designer environment, readers

have gained the necessary knowledge to create and utilize

item based widgets effectively. Additionally, users have
learned how to manage events and signals connected to
item based widgets to facilitate user interaction and

implement desired functionality. Users will be well prepared

to develop a complex yet user friendly interface in Qt

Designer using item based widgets. The inclusion of solved

examples with explanatory comments throughout the

section has further enhanced the understanding and
application of item widget.

--

--

A.I Hub
A.I Hub

Written by A.I Hub

We writes about Data Science | Software Development | Machine Learning | Artificial Intelligence | Ethical Hacking and much more. Unleash your potential with us

No responses yet