Skip to content

Commit 33d23ee

Browse files
authored
Merge branch 'master' into directroy-tree-generator
2 parents ff1660c + aa58c52 commit 33d23ee

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1245
-0
lines changed

python-contact-book/README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# RP Contacts
2+
3+
**RP Contacts** is a Contact Book application built with Python, [PyQt5](https://www.riverbankcomputing.com/static/Docs/PyQt5/index.html), and [SQLite](https://www.sqlite.org/docs.html).
4+
5+
## Running the Application
6+
7+
To run **RP Contacts**, you need to download the source code. Then open a terminal or command-line window and run the following steps:
8+
9+
1. Create and activate a Python virtual environment
10+
11+
```sh
12+
$ cd rpcontacts/
13+
$ python -m venv ./venv
14+
$ source venv/bin/activate
15+
(venv) $
16+
```
17+
18+
2. Install the dependencies
19+
20+
```sh
21+
(venv) $ python -m pip install -r requirements.txt
22+
```
23+
24+
3. Run the application
25+
26+
```sh
27+
(venv) $ python rpcontacts.py
28+
```
29+
30+
**Note:** This application was coded and tested using Python 3.8.5 and PyQt 5.15.2.
31+
32+
## Release History
33+
34+
- 0.1.0
35+
- A work in progress
36+
37+
## About the Author
38+
39+
Leodanis Pozo Ramos – [@lpozo78](https://twitter.com/lpozo78)[email protected]
40+
41+
## License
42+
43+
Distributed under the MIT license.
Binary file not shown.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
PyQt5==5.15.2
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
4+
"""This module provides RP Contacts entry point script."""
5+
6+
from rpcontacts.main import main
7+
8+
if __name__ == "__main__":
9+
main()
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""This module provides the rpcontacts package."""
4+
5+
__version__ = "0.1.0"
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""This module provides a database connection."""
4+
5+
from PyQt5.QtWidgets import QMessageBox
6+
from PyQt5.QtSql import QSqlDatabase, QSqlQuery
7+
8+
9+
def _createContactsTable():
10+
"""Create the contacts table in the database."""
11+
createTableQuery = QSqlQuery()
12+
return createTableQuery.exec(
13+
"""
14+
CREATE TABLE IF NOT EXISTS contacts (
15+
id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL,
16+
name VARCHAR(40) NOT NULL,
17+
job VARCHAR(50),
18+
email VARCHAR(40) NOT NULL
19+
)
20+
"""
21+
)
22+
23+
24+
def createConnection(databaseName):
25+
"""Create and open a database connection."""
26+
connection = QSqlDatabase.addDatabase("QSQLITE")
27+
connection.setDatabaseName(databaseName)
28+
29+
if not connection.open():
30+
QMessageBox.warning(
31+
None,
32+
"RP Contact",
33+
f"Database Error: {connection.lastError().text()}",
34+
)
35+
return False
36+
37+
_createContactsTable()
38+
return True
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""This module provides RP Contacts application."""
4+
5+
import sys
6+
7+
from PyQt5.QtWidgets import QApplication
8+
9+
from .database import createConnection
10+
from .views import Window
11+
12+
13+
def main():
14+
"""RP Contacts main function."""
15+
# Create the application
16+
app = QApplication(sys.argv)
17+
# Connect to the database before creating any window
18+
if not createConnection("contacts.sqlite"):
19+
sys.exit(1)
20+
# Create the main window if the connection succeeded
21+
win = Window()
22+
win.show()
23+
# Run the event loop
24+
sys.exit(app.exec_())
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""This module provides a model to manage the contacts table."""
4+
5+
from PyQt5.QtCore import Qt
6+
from PyQt5.QtSql import QSqlTableModel
7+
8+
9+
class ContactsModel:
10+
def __init__(self):
11+
self.model = self._createModel()
12+
13+
@staticmethod
14+
def _createModel():
15+
"""Create and set up the model."""
16+
tableModel = QSqlTableModel()
17+
tableModel.setTable("contacts")
18+
tableModel.setEditStrategy(QSqlTableModel.OnFieldChange)
19+
tableModel.select()
20+
headers = ("ID", "Name", "Job", "Email")
21+
for columnIndex, header in enumerate(headers):
22+
tableModel.setHeaderData(columnIndex, Qt.Horizontal, header)
23+
return tableModel
24+
25+
def addContact(self, data):
26+
"""Add a contact to the database."""
27+
rows = self.model.rowCount()
28+
self.model.insertRows(rows, 1)
29+
for column_index, field in enumerate(data):
30+
self.model.setData(self.model.index(rows, column_index + 1), field)
31+
self.model.submitAll()
32+
self.model.select()
33+
34+
def deleteContact(self, row):
35+
"""Remove a contact from the database."""
36+
self.model.removeRow(row)
37+
self.model.submitAll()
38+
self.model.select()
39+
40+
def clearContacts(self):
41+
"""Remove all contacts in the database."""
42+
self.model.setEditStrategy(QSqlTableModel.OnManualSubmit)
43+
self.model.removeRows(0, self.model.rowCount())
44+
self.model.submitAll()
45+
self.model.setEditStrategy(QSqlTableModel.OnFieldChange)
46+
self.model.select()
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""This module provides views to manage the contacts table."""
4+
5+
from PyQt5.QtCore import Qt
6+
from PyQt5.QtWidgets import (
7+
QAbstractItemView,
8+
QDialog,
9+
QDialogButtonBox,
10+
QFormLayout,
11+
QHBoxLayout,
12+
QLineEdit,
13+
QMainWindow,
14+
QMessageBox,
15+
QPushButton,
16+
QTableView,
17+
QVBoxLayout,
18+
QWidget,
19+
)
20+
21+
from .model import ContactsModel
22+
23+
24+
class Window(QMainWindow):
25+
"""Main Window."""
26+
27+
def __init__(self, parent=None):
28+
"""Initializer."""
29+
super().__init__(parent)
30+
self.setWindowTitle("RP Contacts")
31+
self.resize(550, 250)
32+
self.centralWidget = QWidget()
33+
self.setCentralWidget(self.centralWidget)
34+
self.layout = QHBoxLayout()
35+
self.centralWidget.setLayout(self.layout)
36+
37+
self.contactsModel = ContactsModel()
38+
self.setupUI()
39+
40+
def setupUI(self):
41+
"""Setup the main window's GUI."""
42+
# Create the table view widget
43+
self.table = QTableView()
44+
self.table.setModel(self.contactsModel.model)
45+
self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
46+
self.table.resizeColumnsToContents()
47+
# Create buttons
48+
self.addButton = QPushButton("Add...")
49+
self.addButton.clicked.connect(self.openAddDialog)
50+
self.deleteButton = QPushButton("Delete")
51+
self.deleteButton.clicked.connect(self.deleteContact)
52+
self.clearAllButton = QPushButton("Clear All")
53+
self.clearAllButton.clicked.connect(self.clearContacts)
54+
# Lay out the GUI
55+
layout = QVBoxLayout()
56+
layout.addWidget(self.addButton)
57+
layout.addWidget(self.deleteButton)
58+
layout.addStretch()
59+
layout.addWidget(self.clearAllButton)
60+
self.layout.addWidget(self.table)
61+
self.layout.addLayout(layout)
62+
63+
def openAddDialog(self):
64+
"""Open the Add Contact dialog."""
65+
dialog = AddDialog(self)
66+
if dialog.exec() == QDialog.Accepted:
67+
self.contactsModel.addContact(dialog.data)
68+
self.table.resizeColumnsToContents()
69+
70+
def deleteContact(self):
71+
"""Delete the selected contact from the database."""
72+
row = self.table.currentIndex().row()
73+
if row < 0:
74+
return
75+
76+
messageBox = QMessageBox.warning(
77+
self,
78+
"Warning!",
79+
"Do you want to remove the selected contact?",
80+
QMessageBox.Ok | QMessageBox.Cancel,
81+
)
82+
83+
if messageBox == QMessageBox.Ok:
84+
self.contactsModel.deleteContact(row)
85+
86+
def clearContacts(self):
87+
"""Remove all contacts from the database."""
88+
messageBox = QMessageBox.warning(
89+
self,
90+
"Warning!",
91+
"Do you want to remove all your contacts?",
92+
QMessageBox.Ok | QMessageBox.Cancel,
93+
)
94+
95+
if messageBox == QMessageBox.Ok:
96+
self.contactsModel.clearContacts()
97+
98+
99+
class AddDialog(QDialog):
100+
"""Add Contact dialog."""
101+
102+
def __init__(self, parent=None):
103+
"""Initializer."""
104+
super().__init__(parent=parent)
105+
self.setWindowTitle("Add Contact")
106+
self.layout = QVBoxLayout()
107+
self.setLayout(self.layout)
108+
self.data = None
109+
110+
self.setupUI()
111+
112+
def setupUI(self):
113+
"""Setup the Add Contact dialog's GUI."""
114+
# Create line edits for data fields
115+
self.nameField = QLineEdit()
116+
self.nameField.setObjectName("Name")
117+
self.jobField = QLineEdit()
118+
self.jobField.setObjectName("Job")
119+
self.emailField = QLineEdit()
120+
self.emailField.setObjectName("Email")
121+
# Lay out the data fields
122+
layout = QFormLayout()
123+
layout.addRow("Name:", self.nameField)
124+
layout.addRow("Job:", self.jobField)
125+
layout.addRow("Email:", self.emailField)
126+
self.layout.addLayout(layout)
127+
# Add standard buttons to the dialog and connect them
128+
self.buttonsBox = QDialogButtonBox(self)
129+
self.buttonsBox.setOrientation(Qt.Horizontal)
130+
self.buttonsBox.setStandardButtons(
131+
QDialogButtonBox.Ok | QDialogButtonBox.Cancel
132+
)
133+
self.buttonsBox.accepted.connect(self.accept)
134+
self.buttonsBox.rejected.connect(self.reject)
135+
self.layout.addWidget(self.buttonsBox)
136+
137+
def accept(self):
138+
"""Accept the data provided through the dialog."""
139+
self.data = []
140+
for field in (self.nameField, self.jobField, self.emailField):
141+
if not field.text():
142+
QMessageBox.critical(
143+
self,
144+
"Error!",
145+
f"You must provide a contact's {field.objectName()}",
146+
)
147+
self.data = None # Reset .data
148+
return
149+
150+
self.data.append(field.text())
151+
152+
if not self.data:
153+
return
154+
155+
super().accept()
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
4+
"""This module provides RP Contacts entry point script."""
5+
6+
from rpcontacts.main import main
7+
8+
if __name__ == "__main__":
9+
main()
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""This module provides the rpcontacts package."""
4+
5+
__version__ = "0.1.0"
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""This module provides RP Contacts application."""
4+
5+
import sys
6+
7+
from PyQt5.QtWidgets import QApplication
8+
9+
from .views import Window
10+
11+
12+
def main():
13+
"""RP Contacts main function."""
14+
# Create the application
15+
app = QApplication(sys.argv)
16+
# Create the main window
17+
win = Window()
18+
win.show()
19+
# Run the event loop
20+
sys.exit(app.exec())
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""This module provides views to manage the contacts table."""
4+
5+
from PyQt5.QtWidgets import (
6+
QHBoxLayout,
7+
QMainWindow,
8+
QWidget,
9+
)
10+
11+
12+
class Window(QMainWindow):
13+
"""Main Window."""
14+
15+
def __init__(self, parent=None):
16+
"""Initializer."""
17+
super().__init__(parent)
18+
self.setWindowTitle("RP Contacts")
19+
self.resize(550, 250)
20+
self.centralWidget = QWidget()
21+
self.setCentralWidget(self.centralWidget)
22+
self.layout = QHBoxLayout()
23+
self.centralWidget.setLayout(self.layout)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
4+
"""This module provides RP Contacts entry point script."""
5+
6+
from rpcontacts.main import main
7+
8+
if __name__ == "__main__":
9+
main()

0 commit comments

Comments
 (0)