未验证 提交 48d692ea 编写于 作者: B Bartosz Zaczyński

Merge branch 'arcade-platformer' of github.com:realpython/materials into arcade-platformer

# RP Tree
RP Tree is a command-line tool to generate a directory tree diagram.
## Run the App
To run **RP Tree**, you need to download the source code. Then open a terminal or command-line window and run the following steps:
1. Create and activate a Python virtual environment
```sh
$ cd rptree_project/
$ python -m venv ./venv
$ source venv/bin/activate
(venv) $
```
2. Run the application
```sh
(venv) $ python tree.py /path/to/directory/
```
**Note:** The `-h` or `--help` option provides help on how to use RP Tree.
To take a quick test on **RP Tree**, you can use the sample `home/` directory provided along with the application's code and run the following command:
```sh
(venv) $ python tree.py ../hello/
../hello/
├── hello/
│ ├── __init__.py
│ └── hello.py
├── tests/
│ └── test_hello.py
├── requirements.txt
├── setup.py
├── README.md
└── LICENSE
```
That's it! You've generated a nice directory tree diagram.
## Current Features
If you run RP Tree with a directory path as an argument, then you get the full directory tree printed on your screen. The default input directory is your current directory.
RP Tree also provides the following options:
- `-v`, `--version` shows the application version and exits
- `-h`, `--help` show a usage message
- `-d`, `--dir-only` generates a directory-only tree
- `-o`, `--output-file` generates a tree and save it to a file in markdown format
## Release History
- 0.1.0
- A work in progress
## About the Author
Leodanis Pozo Ramos - Email: leodanis@realpython.com
# RP Tree
RP Tree is a command-line tool to generate a directory tree diagram.
## Run the App
To run **RP Tree**, you need to download the source code. Then open a terminal or command-line window and run the following steps:
1. Create and activate a Python virtual environment.
```sh
$ cd rptree_project/
$ python -m venv ./venv
$ source venv/bin/activate
(venv) $
```
2. Run the application.
```sh
(venv) $ python tree.py /path/to/directory/
```
**Note:** The `-h` or `--help` option provides help on how to use RP Tree.
To take a quick test on **RP Tree**, you can use the sample `home/` directory provided along with the application's code and run the following command:
```sh
(venv) $ python tree.py ../hello/
../hello/
├── hello/
│ ├── __init__.py
│ └── hello.py
├── tests/
│ └── test_hello.py
├── requirements.txt
├── setup.py
├── README.md
└── LICENSE
```
That's it! You've generated a nice directory tree diagram.
## Current Features
If you run RP Tree with a directory path as an argument, then you get the full directory tree printed on your screen. The default input directory is your current directory.
RP Tree also provides the following options:
- `-v`, `--version` shows the application version and exits
- `-h`, `--help` shows a usage message
- `-d`, `--dir-only` generates a directory-only tree
- `-o`, `--output-file` generates a tree and save it to a file in markdown format
## Release History
- 0.1.0
- A work in progress
## About the Author
Leodanis Pozo Ramos - Email: leodanis@realpython.com
"""Top-level package for RP Tree."""
__version__ = "0.1.0"
"""This module provides the RP Tree CLI."""
import argparse
import pathlib
import sys
from . import __version__
from .rptree import DirectoryTree
def main():
args = parse_cmd_line_arguments()
root_dir = pathlib.Path(args.root_dir)
if not root_dir.is_dir():
print("The specified root directory doesn't exist")
sys.exit()
tree = DirectoryTree(
root_dir, dir_only=args.dir_only, output_file=args.output_file
)
tree.generate()
def parse_cmd_line_arguments():
parser = argparse.ArgumentParser(
prog="tree",
description="RP Tree, a directory tree generator",
epilog="Thanks for using RP Tree!",
)
parser.version = f"RP Tree v{__version__}"
parser.add_argument("-v", "--version", action="version")
parser.add_argument(
"root_dir",
metavar="ROOT_DIR",
nargs="?",
default=".",
help="Generate a full directory tree starting at ROOT_DIR",
)
parser.add_argument(
"-d",
"--dir-only",
action="store_true",
help="Generate a directory-only tree",
)
parser.add_argument(
"-o",
"--output-file",
metavar="OUTPUT_FILE",
nargs="?",
default=sys.stdout,
help="Generate a full directory tree and save it to a file",
)
return parser.parse_args()
"""This module provides RP Tree main module."""
import os
import pathlib
import sys
PIPE = "│"
ELBOW = "└──"
TEE = "├──"
PIPE_PREFIX = "│ "
SPACE_PREFIX = " "
class DirectoryTree:
def __init__(self, root_dir, dir_only=False, output_file=sys.stdout):
self._output_file = output_file
self._generator = _TreeGenerator(root_dir, dir_only)
def generate(self):
tree = self._generator.build_tree()
if self._output_file != sys.stdout:
# Wrap the tree in a markdown code block
tree.insert(0, "```")
tree.append("```")
self._output_file = open(
self._output_file, mode="w", encoding="UTF-8"
)
with self._output_file as stream:
for entry in tree:
print(entry, file=stream)
class _TreeGenerator:
def __init__(self, root_dir, dir_only=False):
self._root_dir = pathlib.Path(root_dir)
self._dir_only = dir_only
self._tree = []
def build_tree(self):
self._tree_head()
self._tree_body(self._root_dir)
return self._tree
def _tree_head(self):
self._tree.append(f"{self._root_dir}{os.sep}")
self._tree.append(PIPE)
def _tree_body(self, directory, prefix=""):
entries = self._prepare_entries(directory)
entries_count = len(entries)
for index, entry in enumerate(entries):
connector = ELBOW if index == entries_count - 1 else TEE
if entry.is_dir():
self._add_directory(
entry, index, entries_count, prefix, connector
)
else:
self._add_file(entry, prefix, connector)
def _prepare_entries(self, directory):
entries = directory.iterdir()
if self._dir_only:
entries = [entry for entry in entries if entry.is_dir()]
return entries
entries = sorted(entries, key=lambda entry: entry.is_file())
return entries
def _add_directory(
self, directory, index, entries_count, prefix, connector
):
self._tree.append(f"{prefix}{connector} {directory.name}{os.sep}")
if index != entries_count - 1:
prefix += PIPE_PREFIX
else:
prefix += SPACE_PREFIX
self._tree_body(
directory=directory,
prefix=prefix,
)
self._tree.append(prefix.rstrip())
def _add_file(self, file, prefix, connector):
self._tree.append(f"{prefix}{connector} {file.name}")
#!/usr/bin/env python3
"""This module provides RP Tree entry point script."""
from rptree.cli import main
if __name__ == "__main__":
main()
"""Top-level package for RP Tree."""
__version__ = "0.1.0"
"""Top-level package for RP Tree."""
__version__ = "0.1.0"
"""This module provides RP Tree main module."""
import os
import pathlib
PIPE = "│"
ELBOW = "└──"
TEE = "├──"
PIPE_PREFIX = "│ "
SPACE_PREFIX = " "
class DirectoryTree:
def __init__(self, root_dir):
self._generator = _TreeGenerator(root_dir)
def generate(self):
tree = self._generator.build_tree()
for entry in tree:
print(entry)
class _TreeGenerator:
def __init__(self, root_dir):
self._root_dir = pathlib.Path(root_dir)
self._tree = []
def build_tree(self):
self._tree_head()
self._tree_body(self._root_dir)
return self._tree
def _tree_head(self):
self._tree.append(f"{self._root_dir}{os.sep}")
self._tree.append(PIPE)
def _tree_body(self, directory, prefix=""):
entries = directory.iterdir()
entries = sorted(entries, key=lambda entry: entry.is_file())
entries_count = len(entries)
for index, entry in enumerate(entries):
connector = ELBOW if index == entries_count - 1 else TEE
if entry.is_dir():
self._add_directory(
entry, index, entries_count, prefix, connector
)
else:
self._add_file(entry, prefix, connector)
def _add_directory(
self, directory, index, entries_count, prefix, connector
):
self._tree.append(f"{prefix}{connector} {directory.name}{os.sep}")
if index != entries_count - 1:
prefix += PIPE_PREFIX
else:
prefix += SPACE_PREFIX
self._tree_body(
directory=directory,
prefix=prefix,
)
self._tree.append(prefix.rstrip())
def _add_file(self, file, prefix, connector):
self._tree.append(f"{prefix}{connector} {file.name}")
"""Top-level package for RP Tree."""
__version__ = "0.1.0"
"""This module provides the RP Tree CLI."""
import argparse
import pathlib
import sys
from . import __version__
from .rptree import DirectoryTree
def main():
args = parse_cmd_line_arguments()
root_dir = pathlib.Path(args.root_dir)
if not root_dir.is_dir():
print("The specified root directory doesn't exist")
sys.exit()
tree = DirectoryTree(root_dir)
tree.generate()
def parse_cmd_line_arguments():
parser = argparse.ArgumentParser(
prog="tree",
description="RP Tree, a directory tree generator",
epilog="Thanks for using RP Tree!",
)
parser.version = f"RP Tree v{__version__}"
parser.add_argument("-v", "--version", action="version")
parser.add_argument(
"root_dir",
metavar="ROOT_DIR",
nargs="?",
default=".",
help="Generate a full directory tree starting at ROOT_DIR",
)
return parser.parse_args()
"""This module provides RP Tree main module."""
import os
import pathlib
PIPE = "│"
ELBOW = "└──"
TEE = "├──"
PIPE_PREFIX = "│ "
SPACE_PREFIX = " "
class DirectoryTree:
def __init__(self, root_dir):
self._generator = _TreeGenerator(root_dir)
def generate(self):
tree = self._generator.build_tree()
for entry in tree:
print(entry)
class _TreeGenerator:
def __init__(self, root_dir):
self._root_dir = pathlib.Path(root_dir)
self._tree = []
def build_tree(self):
self._tree_head()
self._tree_body(self._root_dir)
return self._tree
def _tree_head(self):
self._tree.append(f"{self._root_dir}{os.sep}")
self._tree.append(PIPE)
def _tree_body(self, directory, prefix=""):
entries = directory.iterdir()
entries = sorted(entries, key=lambda entry: entry.is_file())
entries_count = len(entries)
for index, entry in enumerate(entries):
connector = ELBOW if index == entries_count - 1 else TEE
if entry.is_dir():
self._add_directory(
entry, index, entries_count, prefix, connector
)
else:
self._add_file(entry, prefix, connector)
def _add_directory(
self, directory, index, entries_count, prefix, connector
):
self._tree.append(f"{prefix}{connector} {directory.name}{os.sep}")
if index != entries_count - 1:
prefix += PIPE_PREFIX
else:
prefix += SPACE_PREFIX
self._tree_body(
directory=directory,
prefix=prefix,
)
self._tree.append(prefix.rstrip())
def _add_file(self, file, prefix, connector):
self._tree.append(f"{prefix}{connector} {file.name}")
#!/usr/bin/env python3
"""This module provides RP Tree entry point script."""
from rptree.cli import main
if __name__ == "__main__":
main()
"""Top-level package for RP Tree."""
__version__ = "0.1.0"
"""This module provides the RP Tree CLI."""
import argparse
import pathlib
import sys
from . import __version__
from .rptree import DirectoryTree
def main():
args = parse_cmd_line_arguments()
root_dir = pathlib.Path(args.root_dir)
if not root_dir.is_dir():
print("The specified root directory doesn't exist")
sys.exit()
tree = DirectoryTree(root_dir, dir_only=args.dir_only)
tree.generate()
def parse_cmd_line_arguments():
parser = argparse.ArgumentParser(
prog="tree",
description="RP Tree, a directory tree generator",
epilog="Thanks for using RP Tree!",
)
parser.version = f"RP Tree v{__version__}"
parser.add_argument("-v", "--version", action="version")
parser.add_argument(
"root_dir",
metavar="ROOT_DIR",
nargs="?",
default=".",
help="Generate a full directory tree starting at ROOT_DIR",
)
parser.add_argument(
"-d",
"--dir-only",
action="store_true",
help="Generate a directory-only tree",
)
return parser.parse_args()
"""This module provides RP Tree main module."""
import os
import pathlib
PIPE = "│"
ELBOW = "└──"
TEE = "├──"
PIPE_PREFIX = "│ "
SPACE_PREFIX = " "
class DirectoryTree:
def __init__(self, root_dir, dir_only=False):
self._generator = _TreeGenerator(root_dir, dir_only)
def generate(self):
tree = self._generator.build_tree()
for entry in tree:
print(entry)
class _TreeGenerator:
def __init__(self, root_dir, dir_only=False):
self._root_dir = pathlib.Path(root_dir)
self._dir_only = dir_only
self._tree = []
def build_tree(self):
self._tree_head()
self._tree_body(self._root_dir)
return self._tree
def _tree_head(self):
self._tree.append(f"{self._root_dir}{os.sep}")
self._tree.append(PIPE)
def _tree_body(self, directory, prefix=""):
entries = self._prepare_entries(directory)
entries_count = len(entries)
for index, entry in enumerate(entries):
connector = ELBOW if index == entries_count - 1 else TEE
if entry.is_dir():
self._add_directory(
entry, index, entries_count, prefix, connector
)
else:
self._add_file(entry, prefix, connector)
def _prepare_entries(self, directory):
entries = directory.iterdir()
if self._dir_only:
entries = [entry for entry in entries if entry.is_dir()]
return entries
entries = sorted(entries, key=lambda entry: entry.is_file())
return entries
def _add_directory(
self, directory, index, entries_count, prefix, connector
):
self._tree.append(f"{prefix}{connector} {directory.name}{os.sep}")
if index != entries_count - 1:
prefix += PIPE_PREFIX
else:
prefix += SPACE_PREFIX
self._tree_body(
directory=directory,
prefix=prefix,
)
self._tree.append(prefix.rstrip())
def _add_file(self, file, prefix, connector):
self._tree.append(f"{prefix}{connector} {file.name}")
#!/usr/bin/env python3
"""This module provides RP Tree entry point script."""
from rptree.cli import main
if __name__ == "__main__":
main()
```
./
├── rptree/
│ ├── __pycache__/
│ │ ├── rptree.cpython-38.pyc
│ │ ├── __init__.cpython-38.pyc
│ │ └── cli.cpython-38.pyc
│ │
│ ├── rptree.py
│ ├── __init__.py
│ └── cli.py
├── .mypy_cache/
│ ├── 3.8/
│ │ ├── os/
│ │ │ ├── path.meta.json
│ │ │ ├── __init__.meta.json
│ │ │ ├── __init__.data.json
│ │ │ └── path.data.json
│ │ │
│ │ ├── importlib/
│ │ │ ├── abc.data.json
│ │ │ ├── __init__.meta.json
│ │ │ ├── abc.meta.json
│ │ │ └── __init__.data.json
│ │ │
│ │ ├── rptree/
│ │ │ ├── cli.data.json
│ │ │ ├── rptree.meta.json
│ │ │ ├── __init__.meta.json
│ │ │ ├── rptree.data.json
│ │ │ ├── __init__.data.json
│ │ │ └── cli.meta.json
│ │ │
│ │ ├── collections/
│ │ │ ├── abc.data.json
│ │ │ ├── __init__.meta.json
│ │ │ ├── abc.meta.json
│ │ │ └── __init__.data.json
│ │ │
│ │ ├── @plugins_snapshot.json
│ │ ├── argparse.data.json
│ │ ├── typing.data.json
│ │ ├── types.data.json
│ │ ├── abc.data.json
│ │ ├── ast.meta.json
│ │ ├── codecs.data.json
│ │ ├── codecs.meta.json
│ │ ├── builtins.data.json
│ │ ├── io.data.json
│ │ ├── ast.data.json
│ │ ├── argparse.meta.json
│ │ ├── builtins.meta.json
│ │ ├── mmap.meta.json
│ │ ├── posix.meta.json
│ │ ├── genericpath.data.json
│ │ ├── _importlib_modulespec.meta.json
│ │ ├── sys.data.json
│ │ ├── abc.meta.json
│ │ ├── mmap.data.json
│ │ ├── _ast.data.json
│ │ ├── typing.meta.json
│ │ ├── pathlib.meta.json
│ │ ├── _ast.meta.json
│ │ ├── io.meta.json
│ │ ├── types.meta.json
│ │ ├── posix.data.json
│ │ ├── sys.meta.json
│ │ ├── genericpath.meta.json
│ │ ├── _importlib_modulespec.data.json
│ │ └── pathlib.data.json
│ │
│ └── .gitignore
├── README.md
└── tree.py
```
"""Top-level package for RP Tree."""
__version__ = "0.1.0"
"""This module provides the RP Tree CLI."""
import argparse
import pathlib
import sys
from . import __version__
from .rptree import DirectoryTree
def main():
args = parse_cmd_line_arguments()
root_dir = pathlib.Path(args.root_dir)
if not root_dir.is_dir():
print("The specified root directory doesn't exist")
sys.exit()
tree = DirectoryTree(
root_dir, dir_only=args.dir_only, output_file=args.output_file
)
tree.generate()
def parse_cmd_line_arguments():
parser = argparse.ArgumentParser(
prog="tree",
description="RP Tree, a directory tree generator",
epilog="Thanks for using RP Tree!",
)
parser.version = f"RP Tree v{__version__}"
parser.add_argument("-v", "--version", action="version")
parser.add_argument(
"root_dir",
metavar="ROOT_DIR",
nargs="?",
default=".",
help="Generate a full directory tree starting at ROOT_DIR",
)
parser.add_argument(
"-d",
"--dir-only",
action="store_true",
help="Generate a directory-only tree",
)
parser.add_argument(
"-o",
"--output-file",
metavar="OUTPUT_FILE",
nargs="?",
default=sys.stdout,
help="Generate a full directory tree and save it to a file",
)
return parser.parse_args()
"""This module provides RP Tree main module."""
import os
import pathlib
import sys
PIPE = "│"
ELBOW = "└──"
TEE = "├──"
PIPE_PREFIX = "│ "
SPACE_PREFIX = " "
class DirectoryTree:
def __init__(self, root_dir, dir_only=False, output_file=sys.stdout):
self._output_file = output_file
self._generator = _TreeGenerator(root_dir, dir_only)
def generate(self):
tree = self._generator.build_tree()
if self._output_file != sys.stdout:
# Wrap the tree in a markdown code block
tree.insert(0, "```")
tree.append("```")
self._output_file = open(
self._output_file, mode="w", encoding="UTF-8"
)
with self._output_file as stream:
for entry in tree:
print(entry, file=stream)
class _TreeGenerator:
def __init__(self, root_dir, dir_only=False):
self._root_dir = pathlib.Path(root_dir)
self._dir_only = dir_only
self._tree = []
def build_tree(self):
self._tree_head()
self._tree_body(self._root_dir)
return self._tree
def _tree_head(self):
self._tree.append(f"{self._root_dir}{os.sep}")
self._tree.append(PIPE)
def _tree_body(self, directory, prefix=""):
entries = self._prepare_entries(directory)
entries_count = len(entries)
for index, entry in enumerate(entries):
connector = ELBOW if index == entries_count - 1 else TEE
if entry.is_dir():
self._add_directory(
entry, index, entries_count, prefix, connector
)
else:
self._add_file(entry, prefix, connector)
def _prepare_entries(self, directory):
entries = directory.iterdir()
if self._dir_only:
entries = [entry for entry in entries if entry.is_dir()]
return entries
entries = sorted(entries, key=lambda entry: entry.is_file())
return entries
def _add_directory(
self, directory, index, entries_count, prefix, connector
):
self._tree.append(f"{prefix}{connector} {directory.name}{os.sep}")
if index != entries_count - 1:
prefix += PIPE_PREFIX
else:
prefix += SPACE_PREFIX
self._tree_body(
directory=directory,
prefix=prefix,
)
self._tree.append(prefix.rstrip())
def _add_file(self, file, prefix, connector):
self._tree.append(f"{prefix}{connector} {file.name}")
#!/usr/bin/env python3
"""This module provides RP Tree entry point script."""
from rptree.cli import main
if __name__ == "__main__":
main()
The MIT License (MIT)
Copyright (c) 2014 Prahlad Yeri
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
\ No newline at end of file
# RP Contacts
**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).
## Running the Application
To run **RP Contacts**, you need to download the source code. Then open a terminal or command-line window and run the following steps:
1. Create and activate a Python virtual environment
```sh
$ cd rpcontacts/
$ python -m venv ./venv
$ source venv/bin/activate
(venv) $
```
2. Install the dependencies
```sh
(venv) $ python -m pip install -r requirements.txt
```
3. Run the application
```sh
(venv) $ python rpcontacts.py
```
**Note:** This application was coded and tested using Python 3.8.5 and PyQt 5.15.2.
## Release History
- 0.1.0
- A work in progress
## About the Author
Leodanis Pozo Ramos – [@lpozo78](https://twitter.com/lpozo78) – leodanis@realpython.com
## License
Distributed under the MIT license.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""This module provides RP Contacts entry point script."""
from rpcontacts.main import main
if __name__ == "__main__":
main()
# -*- coding: utf-8 -*-
"""This module provides the rpcontacts package."""
__version__ = "0.1.0"
# -*- coding: utf-8 -*-
"""This module provides a database connection."""
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtSql import QSqlDatabase, QSqlQuery
def _createContactsTable():
"""Create the contacts table in the database."""
createTableQuery = QSqlQuery()
return createTableQuery.exec(
"""
CREATE TABLE IF NOT EXISTS contacts (
id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL,
name VARCHAR(40) NOT NULL,
job VARCHAR(50),
email VARCHAR(40) NOT NULL
)
"""
)
def createConnection(databaseName):
"""Create and open a database connection."""
connection = QSqlDatabase.addDatabase("QSQLITE")
connection.setDatabaseName(databaseName)
if not connection.open():
QMessageBox.warning(
None,
"RP Contact",
f"Database Error: {connection.lastError().text()}",
)
return False
_createContactsTable()
return True
# -*- coding: utf-8 -*-
"""This module provides RP Contacts application."""
import sys
from PyQt5.QtWidgets import QApplication
from .database import createConnection
from .views import Window
def main():
"""RP Contacts main function."""
# Create the application
app = QApplication(sys.argv)
# Connect to the database before creating any window
if not createConnection("contacts.sqlite"):
sys.exit(1)
# Create the main window if the connection succeeded
win = Window()
win.show()
# Run the event loop
sys.exit(app.exec_())
# -*- coding: utf-8 -*-
"""This module provides a model to manage the contacts table."""
from PyQt5.QtCore import Qt
from PyQt5.QtSql import QSqlTableModel
class ContactsModel:
def __init__(self):
self.model = self._createModel()
@staticmethod
def _createModel():
"""Create and set up the model."""
tableModel = QSqlTableModel()
tableModel.setTable("contacts")
tableModel.setEditStrategy(QSqlTableModel.OnFieldChange)
tableModel.select()
headers = ("ID", "Name", "Job", "Email")
for columnIndex, header in enumerate(headers):
tableModel.setHeaderData(columnIndex, Qt.Horizontal, header)
return tableModel
def addContact(self, data):
"""Add a contact to the database."""
rows = self.model.rowCount()
self.model.insertRows(rows, 1)
for column_index, field in enumerate(data):
self.model.setData(self.model.index(rows, column_index + 1), field)
self.model.submitAll()
self.model.select()
def deleteContact(self, row):
"""Remove a contact from the database."""
self.model.removeRow(row)
self.model.submitAll()
self.model.select()
def clearContacts(self):
"""Remove all contacts in the database."""
self.model.setEditStrategy(QSqlTableModel.OnManualSubmit)
self.model.removeRows(0, self.model.rowCount())
self.model.submitAll()
self.model.setEditStrategy(QSqlTableModel.OnFieldChange)
self.model.select()
# -*- coding: utf-8 -*-
"""This module provides views to manage the contacts table."""
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QAbstractItemView,
QDialog,
QDialogButtonBox,
QFormLayout,
QHBoxLayout,
QLineEdit,
QMainWindow,
QMessageBox,
QPushButton,
QTableView,
QVBoxLayout,
QWidget,
)
from .model import ContactsModel
class Window(QMainWindow):
"""Main Window."""
def __init__(self, parent=None):
"""Initializer."""
super().__init__(parent)
self.setWindowTitle("RP Contacts")
self.resize(550, 250)
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.layout = QHBoxLayout()
self.centralWidget.setLayout(self.layout)
self.contactsModel = ContactsModel()
self.setupUI()
def setupUI(self):
"""Setup the main window's GUI."""
# Create the table view widget
self.table = QTableView()
self.table.setModel(self.contactsModel.model)
self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
self.table.resizeColumnsToContents()
# Create buttons
self.addButton = QPushButton("Add...")
self.addButton.clicked.connect(self.openAddDialog)
self.deleteButton = QPushButton("Delete")
self.deleteButton.clicked.connect(self.deleteContact)
self.clearAllButton = QPushButton("Clear All")
self.clearAllButton.clicked.connect(self.clearContacts)
# Lay out the GUI
layout = QVBoxLayout()
layout.addWidget(self.addButton)
layout.addWidget(self.deleteButton)
layout.addStretch()
layout.addWidget(self.clearAllButton)
self.layout.addWidget(self.table)
self.layout.addLayout(layout)
def openAddDialog(self):
"""Open the Add Contact dialog."""
dialog = AddDialog(self)
if dialog.exec() == QDialog.Accepted:
self.contactsModel.addContact(dialog.data)
self.table.resizeColumnsToContents()
def deleteContact(self):
"""Delete the selected contact from the database."""
row = self.table.currentIndex().row()
if row < 0:
return
messageBox = QMessageBox.warning(
self,
"Warning!",
"Do you want to remove the selected contact?",
QMessageBox.Ok | QMessageBox.Cancel,
)
if messageBox == QMessageBox.Ok:
self.contactsModel.deleteContact(row)
def clearContacts(self):
"""Remove all contacts from the database."""
messageBox = QMessageBox.warning(
self,
"Warning!",
"Do you want to remove all your contacts?",
QMessageBox.Ok | QMessageBox.Cancel,
)
if messageBox == QMessageBox.Ok:
self.contactsModel.clearContacts()
class AddDialog(QDialog):
"""Add Contact dialog."""
def __init__(self, parent=None):
"""Initializer."""
super().__init__(parent=parent)
self.setWindowTitle("Add Contact")
self.layout = QVBoxLayout()
self.setLayout(self.layout)
self.data = None
self.setupUI()
def setupUI(self):
"""Setup the Add Contact dialog's GUI."""
# Create line edits for data fields
self.nameField = QLineEdit()
self.nameField.setObjectName("Name")
self.jobField = QLineEdit()
self.jobField.setObjectName("Job")
self.emailField = QLineEdit()
self.emailField.setObjectName("Email")
# Lay out the data fields
layout = QFormLayout()
layout.addRow("Name:", self.nameField)
layout.addRow("Job:", self.jobField)
layout.addRow("Email:", self.emailField)
self.layout.addLayout(layout)
# Add standard buttons to the dialog and connect them
self.buttonsBox = QDialogButtonBox(self)
self.buttonsBox.setOrientation(Qt.Horizontal)
self.buttonsBox.setStandardButtons(
QDialogButtonBox.Ok | QDialogButtonBox.Cancel
)
self.buttonsBox.accepted.connect(self.accept)
self.buttonsBox.rejected.connect(self.reject)
self.layout.addWidget(self.buttonsBox)
def accept(self):
"""Accept the data provided through the dialog."""
self.data = []
for field in (self.nameField, self.jobField, self.emailField):
if not field.text():
QMessageBox.critical(
self,
"Error!",
f"You must provide a contact's {field.objectName()}",
)
self.data = None # Reset .data
return
self.data.append(field.text())
if not self.data:
return
super().accept()
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""This module provides RP Contacts entry point script."""
from rpcontacts.main import main
if __name__ == "__main__":
main()
# -*- coding: utf-8 -*-
"""This module provides the rpcontacts package."""
__version__ = "0.1.0"
# -*- coding: utf-8 -*-
"""This module provides RP Contacts application."""
import sys
from PyQt5.QtWidgets import QApplication
from .views import Window
def main():
"""RP Contacts main function."""
# Create the application
app = QApplication(sys.argv)
# Create the main window
win = Window()
win.show()
# Run the event loop
sys.exit(app.exec())
# -*- coding: utf-8 -*-
"""This module provides views to manage the contacts table."""
from PyQt5.QtWidgets import (
QHBoxLayout,
QMainWindow,
QWidget,
)
class Window(QMainWindow):
"""Main Window."""
def __init__(self, parent=None):
"""Initializer."""
super().__init__(parent)
self.setWindowTitle("RP Contacts")
self.resize(550, 250)
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.layout = QHBoxLayout()
self.centralWidget.setLayout(self.layout)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""This module provides RP Contacts entry point script."""
from rpcontacts.main import main
if __name__ == "__main__":
main()
# -*- coding: utf-8 -*-
"""This module provides the rpcontacts package."""
__version__ = "0.1.0"
# -*- coding: utf-8 -*-
"""This module provides RP Contacts application."""
import sys
from PyQt5.QtWidgets import QApplication
from .views import Window
def main():
"""RP Contacts main function."""
# Create the application
app = QApplication(sys.argv)
# Create the main window
win = Window()
win.show()
# Run the event loop
sys.exit(app.exec())
# -*- coding: utf-8 -*-
"""This module provides views to manage the contacts table."""
from PyQt5.QtWidgets import (
QAbstractItemView,
QHBoxLayout,
QMainWindow,
QPushButton,
QTableView,
QVBoxLayout,
QWidget,
)
class Window(QMainWindow):
"""Main Window."""
def __init__(self, parent=None):
"""Initializer."""
super().__init__(parent)
self.setWindowTitle("RP Contacts")
self.resize(550, 250)
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.layout = QHBoxLayout()
self.centralWidget.setLayout(self.layout)
self.setupUI()
def setupUI(self):
"""Setup the main window's GUI."""
# Create the table view widget
self.table = QTableView()
self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
self.table.resizeColumnsToContents()
# Create buttons
self.addButton = QPushButton("Add...")
self.deleteButton = QPushButton("Delete")
self.clearAllButton = QPushButton("Clear All")
# Lay out the GUI
layout = QVBoxLayout()
layout.addWidget(self.addButton)
layout.addWidget(self.deleteButton)
layout.addStretch()
layout.addWidget(self.clearAllButton)
self.layout.addWidget(self.table)
self.layout.addLayout(layout)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""This module provides RP Contacts entry point script."""
from rpcontacts.main import main
if __name__ == "__main__":
main()
# -*- coding: utf-8 -*-
"""This module provides the rpcontacts package."""
__version__ = "0.1.0"
# -*- coding: utf-8 -*-
"""This module provides a database connection."""
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtSql import QSqlDatabase, QSqlQuery
def _createContactsTable():
"""Create the contacts table in the database."""
createTableQuery = QSqlQuery()
return createTableQuery.exec(
"""
CREATE TABLE IF NOT EXISTS contacts (
id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL,
name VARCHAR(40) NOT NULL,
job VARCHAR(50),
email VARCHAR(40) NOT NULL
)
"""
)
def createConnection(databaseName):
"""Create and open a database connection."""
connection = QSqlDatabase.addDatabase("QSQLITE")
connection.setDatabaseName(databaseName)
if not connection.open():
QMessageBox.warning(
None,
"RP Contact",
f"Database Error: {connection.lastError().text()}",
)
return False
_createContactsTable()
return True
# -*- coding: utf-8 -*-
"""This module provides RP Contacts application."""
import sys
from PyQt5.QtWidgets import QApplication
from .database import createConnection
from .views import Window
def main():
"""RP Contacts main function."""
# Create the application
app = QApplication(sys.argv)
# Connect to the database before creating any window
if not createConnection("contacts.sqlite"):
sys.exit(1)
# Create the main window if the connection succeeded
win = Window()
win.show()
# Run the event loop
sys.exit(app.exec_())
# -*- coding: utf-8 -*-
"""This module provides views to manage the contacts table."""
from PyQt5.QtWidgets import (
QAbstractItemView,
QHBoxLayout,
QMainWindow,
QPushButton,
QTableView,
QVBoxLayout,
QWidget,
)
class Window(QMainWindow):
"""Main Window."""
def __init__(self, parent=None):
"""Initializer."""
super().__init__(parent)
self.setWindowTitle("RP Contacts")
self.resize(550, 250)
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.layout = QHBoxLayout()
self.centralWidget.setLayout(self.layout)
self.setupUI()
def setupUI(self):
"""Setup the main window's GUI."""
# Create the table view widget
self.table = QTableView()
self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
self.table.resizeColumnsToContents()
# Create buttons
self.addButton = QPushButton("Add...")
self.deleteButton = QPushButton("Delete")
self.clearAllButton = QPushButton("Clear All")
# Lay out the GUI
layout = QVBoxLayout()
layout.addWidget(self.addButton)
layout.addWidget(self.deleteButton)
layout.addStretch()
layout.addWidget(self.clearAllButton)
self.layout.addWidget(self.table)
self.layout.addLayout(layout)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""This module provides RP Contacts entry point script."""
from rpcontacts.main import main
if __name__ == "__main__":
main()
# -*- coding: utf-8 -*-
"""This module provides the rpcontacts package."""
__version__ = "0.1.0"
# -*- coding: utf-8 -*-
"""This module provides a database connection."""
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtSql import QSqlDatabase, QSqlQuery
def _createContactsTable():
"""Create the contacts table in the database."""
createTableQuery = QSqlQuery()
return createTableQuery.exec(
"""
CREATE TABLE IF NOT EXISTS contacts (
id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL,
name VARCHAR(40) NOT NULL,
job VARCHAR(50),
email VARCHAR(40) NOT NULL
)
"""
)
def createConnection(databaseName):
"""Create and open a database connection."""
connection = QSqlDatabase.addDatabase("QSQLITE")
connection.setDatabaseName(databaseName)
if not connection.open():
QMessageBox.warning(
None,
"RP Contact",
f"Database Error: {connection.lastError().text()}",
)
return False
_createContactsTable()
return True
# -*- coding: utf-8 -*-
"""This module provides RP Contacts application."""
import sys
from PyQt5.QtWidgets import QApplication
from .database import createConnection
from .views import Window
def main():
"""RP Contacts main function."""
# Create the application
app = QApplication(sys.argv)
# Connect to the database before creating any window
if not createConnection("contacts.sqlite"):
sys.exit(1)
# Create the main window if the connection succeeded
win = Window()
win.show()
# Run the event loop
sys.exit(app.exec_())
# -*- coding: utf-8 -*-
"""This module provides a model to manage the contacts table."""
from PyQt5.QtCore import Qt
from PyQt5.QtSql import QSqlTableModel
class ContactsModel:
def __init__(self):
self.model = self._createModel()
@staticmethod
def _createModel():
"""Create and set up the model."""
tableModel = QSqlTableModel()
tableModel.setTable("contacts")
tableModel.setEditStrategy(QSqlTableModel.OnFieldChange)
tableModel.select()
headers = ("ID", "Name", "Job", "Email")
for columnIndex, header in enumerate(headers):
tableModel.setHeaderData(columnIndex, Qt.Horizontal, header)
return tableModel
# -*- coding: utf-8 -*-
"""This module provides views to manage the contacts table."""
from PyQt5.QtWidgets import (
QAbstractItemView,
QHBoxLayout,
QMainWindow,
QPushButton,
QTableView,
QVBoxLayout,
QWidget,
)
from .model import ContactsModel
class Window(QMainWindow):
"""Main Window."""
def __init__(self, parent=None):
"""Initializer."""
super().__init__(parent)
self.setWindowTitle("RP Contacts")
self.resize(550, 250)
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.layout = QHBoxLayout()
self.centralWidget.setLayout(self.layout)
self.contactsModel = ContactsModel()
self.setupUI()
def setupUI(self):
"""Setup the main window's GUI."""
# Create the table view widget
self.table = QTableView()
self.table.setModel(self.contactsModel.model)
self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
self.table.resizeColumnsToContents()
# Create buttons
self.addButton = QPushButton("Add...")
self.deleteButton = QPushButton("Delete")
self.clearAllButton = QPushButton("Clear All")
# Lay out the GUI
layout = QVBoxLayout()
layout.addWidget(self.addButton)
layout.addWidget(self.deleteButton)
layout.addStretch()
layout.addWidget(self.clearAllButton)
self.layout.addWidget(self.table)
self.layout.addLayout(layout)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""This module provides RP Contacts entry point script."""
from rpcontacts.main import main
if __name__ == "__main__":
main()
# -*- coding: utf-8 -*-
"""This module provides the rpcontacts package."""
__version__ = "0.1.0"
# -*- coding: utf-8 -*-
"""This module provides a database connection."""
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtSql import QSqlDatabase, QSqlQuery
def _createContactsTable():
"""Create the contacts table in the database."""
createTableQuery = QSqlQuery()
return createTableQuery.exec(
"""
CREATE TABLE IF NOT EXISTS contacts (
id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL,
name VARCHAR(40) NOT NULL,
job VARCHAR(50),
email VARCHAR(40) NOT NULL
)
"""
)
def createConnection(databaseName):
"""Create and open a database connection."""
connection = QSqlDatabase.addDatabase("QSQLITE")
connection.setDatabaseName(databaseName)
if not connection.open():
QMessageBox.warning(
None,
"RP Contact",
f"Database Error: {connection.lastError().text()}",
)
return False
_createContactsTable()
return True
# -*- coding: utf-8 -*-
"""This module provides RP Contacts application."""
import sys
from PyQt5.QtWidgets import QApplication
from .database import createConnection
from .views import Window
def main():
"""RP Contacts main function."""
# Create the application
app = QApplication(sys.argv)
# Connect to the database before creating any window
if not createConnection("contacts.sqlite"):
sys.exit(1)
# Create the main window if the connection succeeded
win = Window()
win.show()
# Run the event loop
sys.exit(app.exec_())
# -*- coding: utf-8 -*-
"""This module provides a model to manage the contacts table."""
from PyQt5.QtCore import Qt
from PyQt5.QtSql import QSqlTableModel
class ContactsModel:
def __init__(self):
self.model = self._createModel()
@staticmethod
def _createModel():
"""Create and set up the model."""
tableModel = QSqlTableModel()
tableModel.setTable("contacts")
tableModel.setEditStrategy(QSqlTableModel.OnFieldChange)
tableModel.select()
headers = ("ID", "Name", "Job", "Email")
for columnIndex, header in enumerate(headers):
tableModel.setHeaderData(columnIndex, Qt.Horizontal, header)
return tableModel
def addContact(self, data):
"""Add a contact to the database."""
rows = self.model.rowCount()
self.model.insertRows(rows, 1)
for column_index, field in enumerate(data):
self.model.setData(self.model.index(rows, column_index + 1), field)
self.model.submitAll()
self.model.select()
# -*- coding: utf-8 -*-
"""This module provides views to manage the contacts table."""
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QAbstractItemView,
QDialog,
QDialogButtonBox,
QFormLayout,
QHBoxLayout,
QLineEdit,
QMainWindow,
QMessageBox,
QPushButton,
QTableView,
QVBoxLayout,
QWidget,
)
from .model import ContactsModel
class Window(QMainWindow):
"""Main Window."""
def __init__(self, parent=None):
"""Initializer."""
super().__init__(parent)
self.setWindowTitle("RP Contacts")
self.resize(550, 250)
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.layout = QHBoxLayout()
self.centralWidget.setLayout(self.layout)
self.contactsModel = ContactsModel()
self.setupUI()
def setupUI(self):
"""Setup the main window's GUI."""
# Create the table view widget
self.table = QTableView()
self.table.setModel(self.contactsModel.model)
self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
self.table.resizeColumnsToContents()
# Create buttons
self.addButton = QPushButton("Add...")
self.addButton.clicked.connect(self.openAddDialog)
self.deleteButton = QPushButton("Delete")
self.clearAllButton = QPushButton("Clear All")
# Lay out the GUI
layout = QVBoxLayout()
layout.addWidget(self.addButton)
layout.addWidget(self.deleteButton)
layout.addStretch()
layout.addWidget(self.clearAllButton)
self.layout.addWidget(self.table)
self.layout.addLayout(layout)
def openAddDialog(self):
"""Open the Add Contact dialog."""
dialog = AddDialog(self)
if dialog.exec() == QDialog.Accepted:
self.contactsModel.addContact(dialog.data)
self.table.resizeColumnsToContents()
class AddDialog(QDialog):
"""Add Contact dialog."""
def __init__(self, parent=None):
"""Initializer."""
super().__init__(parent=parent)
self.setWindowTitle("Add Contact")
self.layout = QVBoxLayout()
self.setLayout(self.layout)
self.data = None
self.setupUI()
def setupUI(self):
"""Setup the Add Contact dialog's GUI."""
# Create line edits for data fields
self.nameField = QLineEdit()
self.nameField.setObjectName("Name")
self.jobField = QLineEdit()
self.jobField.setObjectName("Job")
self.emailField = QLineEdit()
self.emailField.setObjectName("Email")
# Lay out the data fields
layout = QFormLayout()
layout.addRow("Name:", self.nameField)
layout.addRow("Job:", self.jobField)
layout.addRow("Email:", self.emailField)
self.layout.addLayout(layout)
# Add standard buttons to the dialog and connect them
self.buttonsBox = QDialogButtonBox(self)
self.buttonsBox.setOrientation(Qt.Horizontal)
self.buttonsBox.setStandardButtons(
QDialogButtonBox.Ok | QDialogButtonBox.Cancel
)
self.buttonsBox.accepted.connect(self.accept)
self.buttonsBox.rejected.connect(self.reject)
self.layout.addWidget(self.buttonsBox)
def accept(self):
"""Accept the data provided through the dialog."""
self.data = []
for field in (self.nameField, self.jobField, self.emailField):
if not field.text():
QMessageBox.critical(
self,
"Error!",
f"You must provide a contact's {field.objectName()}",
)
self.data = None # Reset .data
return
self.data.append(field.text())
if not self.data:
return
super().accept()
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""This module provides RP Contacts entry point script."""
from rpcontacts.main import main
if __name__ == "__main__":
main()
# -*- coding: utf-8 -*-
"""This module provides the rpcontacts package."""
__version__ = "0.1.0"
# -*- coding: utf-8 -*-
"""This module provides a database connection."""
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtSql import QSqlDatabase, QSqlQuery
def _createContactsTable():
"""Create the contacts table in the database."""
createTableQuery = QSqlQuery()
return createTableQuery.exec(
"""
CREATE TABLE IF NOT EXISTS contacts (
id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL,
name VARCHAR(40) NOT NULL,
job VARCHAR(50),
email VARCHAR(40) NOT NULL
)
"""
)
def createConnection(databaseName):
"""Create and open a database connection."""
connection = QSqlDatabase.addDatabase("QSQLITE")
connection.setDatabaseName(databaseName)
if not connection.open():
QMessageBox.warning(
None,
"RP Contact",
f"Database Error: {connection.lastError().text()}",
)
return False
_createContactsTable()
return True
# -*- coding: utf-8 -*-
"""This module provides RP Contacts application."""
import sys
from PyQt5.QtWidgets import QApplication
from .database import createConnection
from .views import Window
def main():
"""RP Contacts main function."""
# Create the application
app = QApplication(sys.argv)
# Connect to the database before creating any window
if not createConnection("contacts.sqlite"):
sys.exit(1)
# Create the main window if the connection succeeded
win = Window()
win.show()
# Run the event loop
sys.exit(app.exec_())
# -*- coding: utf-8 -*-
"""This module provides a model to manage the contacts table."""
from PyQt5.QtCore import Qt
from PyQt5.QtSql import QSqlTableModel
class ContactsModel:
def __init__(self):
self.model = self._createModel()
@staticmethod
def _createModel():
"""Create and set up the model."""
tableModel = QSqlTableModel()
tableModel.setTable("contacts")
tableModel.setEditStrategy(QSqlTableModel.OnFieldChange)
tableModel.select()
headers = ("ID", "Name", "Job", "Email")
for columnIndex, header in enumerate(headers):
tableModel.setHeaderData(columnIndex, Qt.Horizontal, header)
return tableModel
def addContact(self, data):
"""Add a contact to the database."""
rows = self.model.rowCount()
self.model.insertRows(rows, 1)
for column_index, field in enumerate(data):
self.model.setData(self.model.index(rows, column_index + 1), field)
self.model.submitAll()
self.model.select()
def deleteContact(self, row):
"""Remove a contact from the database."""
self.model.removeRow(row)
self.model.submitAll()
self.model.select()
def clearContacts(self):
"""Remove all contacts in the database."""
self.model.setEditStrategy(QSqlTableModel.OnManualSubmit)
self.model.removeRows(0, self.model.rowCount())
self.model.submitAll()
self.model.setEditStrategy(QSqlTableModel.OnFieldChange)
self.model.select()
# -*- coding: utf-8 -*-
"""This module provides views to manage the contacts table."""
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QAbstractItemView,
QDialog,
QDialogButtonBox,
QFormLayout,
QHBoxLayout,
QLineEdit,
QMainWindow,
QMessageBox,
QPushButton,
QTableView,
QVBoxLayout,
QWidget,
)
from .model import ContactsModel
class Window(QMainWindow):
"""Main Window."""
def __init__(self, parent=None):
"""Initializer."""
super().__init__(parent)
self.setWindowTitle("RP Contacts")
self.resize(550, 250)
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.layout = QHBoxLayout()
self.centralWidget.setLayout(self.layout)
self.contactsModel = ContactsModel()
self.setupUI()
def setupUI(self):
"""Setup the main window's GUI."""
# Create the table view widget
self.table = QTableView()
self.table.setModel(self.contactsModel.model)
self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
self.table.resizeColumnsToContents()
# Create buttons
self.addButton = QPushButton("Add...")
self.addButton.clicked.connect(self.openAddDialog)
self.deleteButton = QPushButton("Delete")
self.deleteButton.clicked.connect(self.deleteContact)
self.clearAllButton = QPushButton("Clear All")
self.clearAllButton.clicked.connect(self.clearContacts)
# Lay out the GUI
layout = QVBoxLayout()
layout.addWidget(self.addButton)
layout.addWidget(self.deleteButton)
layout.addStretch()
layout.addWidget(self.clearAllButton)
self.layout.addWidget(self.table)
self.layout.addLayout(layout)
def openAddDialog(self):
"""Open the Add Contact dialog."""
dialog = AddDialog(self)
if dialog.exec() == QDialog.Accepted:
self.contactsModel.addContact(dialog.data)
self.table.resizeColumnsToContents()
def deleteContact(self):
"""Delete the selected contact from the database."""
row = self.table.currentIndex().row()
if row < 0:
return
messageBox = QMessageBox.warning(
self,
"Warning!",
"Do you want to remove the selected contact?",
QMessageBox.Ok | QMessageBox.Cancel,
)
if messageBox == QMessageBox.Ok:
self.contactsModel.deleteContact(row)
def clearContacts(self):
"""Remove all contacts from the database."""
messageBox = QMessageBox.warning(
self,
"Warning!",
"Do you want to remove all your contacts?",
QMessageBox.Ok | QMessageBox.Cancel,
)
if messageBox == QMessageBox.Ok:
self.contactsModel.clearContacts()
class AddDialog(QDialog):
"""Add Contact dialog."""
def __init__(self, parent=None):
"""Initializer."""
super().__init__(parent=parent)
self.setWindowTitle("Add Contact")
self.layout = QVBoxLayout()
self.setLayout(self.layout)
self.data = None
self.setupUI()
def setupUI(self):
"""Setup the Add Contact dialog's GUI."""
# Create line edits for data fields
self.nameField = QLineEdit()
self.nameField.setObjectName("Name")
self.jobField = QLineEdit()
self.jobField.setObjectName("Job")
self.emailField = QLineEdit()
self.emailField.setObjectName("Email")
# Lay out the data fields
layout = QFormLayout()
layout.addRow("Name:", self.nameField)
layout.addRow("Job:", self.jobField)
layout.addRow("Email:", self.emailField)
self.layout.addLayout(layout)
# Add standard buttons to the dialog and connect them
self.buttonsBox = QDialogButtonBox(self)
self.buttonsBox.setOrientation(Qt.Horizontal)
self.buttonsBox.setStandardButtons(
QDialogButtonBox.Ok | QDialogButtonBox.Cancel
)
self.buttonsBox.accepted.connect(self.accept)
self.buttonsBox.rejected.connect(self.reject)
self.layout.addWidget(self.buttonsBox)
def accept(self):
"""Accept the data provided through the dialog."""
self.data = []
for field in (self.nameField, self.jobField, self.emailField):
if not field.text():
QMessageBox.critical(
self,
"Error!",
f"You must provide a contact's {field.objectName()}",
)
self.data = None # Reset .data
return
self.data.append(field.text())
if not self.data:
return
super().accept()
# Finding the Shortest Path on a Map
Code supplementing the [Using the Python "heapq" Module and Priority Queues](https://realpython.com/python-heapq-module/) article.
## Usage
Run the following program:
```shell
$ python shortest-path.py
```
It will print out a map with the shortest path from the top-left corner to the bottom-right corner indicated with `@`.
## Changing the Map
In order to change the map the robot uses, modify the triple-quoted string that is assigned to `map` near the top of the file. Anything except `X` is interpreted as free from obstacles. Using `.` makes the map easier to read directly. You can make the map bigger, and a lot more complicated.
If there are so many obstacles that make finding a path from the top-left corner to the bottom right corner impossible, the program will raise an exception.
## Changing the Rules
If you want to play around with the code, here are some changes that you can make:
* How would you change the code so that the robot cannot go diagonally?
* How would you change the code so that the robot can only move right or down, but never left or up?
* (Harder) How would you change the code so that every step consumes energy, squares with `*` give energy, and the robot cannot move if it is out of energy?
* (Challenge) How would you change the code so that areas marked with `#` are not obstacles, but take twice as long to move through?
import heapq
map = """\
.......X..
.......X..
....XXXX..
..........
..........
"""
def parse_map(map):
lines = map.splitlines()
origin = 0, 0
destination = len(lines[-1]) - 1, len(lines) - 1
return lines, origin, destination
def is_valid(lines, position):
x, y = position
if not (0 <= y < len(lines) and 0 <= x < len(lines[y])):
return False
if lines[y][x] == "X":
return False
return True
def get_neighbors(lines, current):
x, y = current
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
if dx == 0 and dy == 0:
continue
position = x + dx, y + dy
if is_valid(lines, position):
yield position
def get_shorter_paths(tentative, positions, through):
path = tentative[through] + [through]
for position in positions:
if position in tentative and len(tentative[position]) <= len(path):
continue
yield position, path
def find_path(map):
lines, origin, destination = parse_map(map)
tentative = {origin: []}
candidates = [(0, origin)]
certain = set()
while destination not in certain and len(candidates) > 0:
_ignored, current = heapq.heappop(candidates)
if current in certain:
continue
certain.add(current)
neighbors = set(get_neighbors(lines, current)) - certain
shorter = get_shorter_paths(tentative, neighbors, current)
for neighbor, path in shorter:
tentative[neighbor] = path
heapq.heappush(candidates, (len(path), neighbor))
if destination in tentative:
return tentative[destination] + [destination]
else:
raise ValueError("no path")
def show_path(path, map):
lines = map.splitlines()
for x, y in path:
lines[y] = lines[y][:x] + "@" + lines[y][x + 1 :]
return "\n".join(lines) + "\n"
path = find_path(map)
print(show_path(path, map))
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册