50 Commits

Author SHA1 Message Date
a3d6a85516 Includes. 2025-03-01 17:08:44 -05:00
a91352ffcd Make Pixel static. 2025-03-01 16:49:15 -05:00
fc0fd98426 Clean up comments. 2025-03-01 16:15:21 -05:00
1a7da67bdb Clean up flush callback. 2025-03-01 15:43:40 -05:00
00ea5dae20 Refactor ownership of draw buffer data. 2025-03-01 10:34:24 -05:00
63899a606a Clean up some comments. 2025-02-26 19:26:53 -05:00
25b4564a8b Fix ESP logging tags. 2025-02-16 14:40:53 -05:00
e2bb406139 Finish cleanup. 2025-02-16 14:32:00 -05:00
96b6a8bec9 Rename project. 2025-02-16 11:48:15 -05:00
509b57fedb Add time_keeper.h
+ Refactor most classes to header only.
2025-02-16 11:24:35 -05:00
75b51f0c7c Cleanup remaining warnings. 2025-02-16 09:41:09 -05:00
74404b1a44 Make TimeKeeper a static member of Display. 2025-02-16 09:12:24 -05:00
9140ba5fb4 Add Timer and TimeKeeper to Display. 2025-02-16 08:58:40 -05:00
c9ec16d70c More cleanup. 2025-02-16 07:25:02 -05:00
5c61fbd378 Clean code. 2025-02-16 06:38:47 -05:00
8636de8f2f Pass RST pin through I2C. 2025-02-15 18:16:25 -05:00
6493988324 Add panel.h, panel.cpp. 2025-02-15 17:52:33 -05:00
64d817e362 Move I2C into header file. 2025-02-15 17:44:58 -05:00
b3d830cdeb Add IPanelDevice. 2025-02-15 17:12:45 -05:00
670a523a16 Clean code. 2025-02-15 14:13:42 -05:00
cc5bffd1e7 Store configs used in ctors. 2025-02-15 10:26:27 -05:00
03784ac097 Move classes to separate files. 2025-02-15 10:11:49 -05:00
9e912048ab Checkpoint adding SSD1306 and PanelDevice. 2025-02-15 09:41:13 -05:00
0743fc4a5e Factor out Panel. 2025-02-14 17:47:44 -05:00
046dfbb6e6 Add Display::set_text. 2025-02-14 17:19:13 -05:00
ef7a027cf0 Factor out I2C. 2025-02-14 16:49:38 -05:00
2dd099f26e Improve ScopedLock. 2025-02-14 16:33:41 -05:00
8aaed133e8 Update Display getters. 2025-02-14 15:58:37 -05:00
58a83590ca Add ScopedLock for LVGL. 2025-02-14 15:50:35 -05:00
dd5335815c Checkpoint 2025-02-14 15:02:49 -05:00
e9d5ef46d1 Replace lcd project 2025-02-13 19:31:58 -05:00
356d8ccd9a WIP debug I2C driver error
E (413) i2c: CONFLICT! driver_ng is not allowed to be used with this old driver
2025-02-09 20:04:25 -05:00
4063921340 Add I2C scanner example. 2025-02-09 11:59:38 -05:00
043fa2fabb WIP lcd 2025-02-09 01:18:06 -05:00
6cd7d7db29 [esp] Port temp-humidity-web example to cmake. 2025-02-08 12:50:06 -05:00
17c559a31f [esp] Add ESP-IDF cmake example. 2025-02-08 12:47:01 -05:00
e6ba60da89 [esp] Add temperature and humidity example. 2025-02-01 23:49:53 -05:00
8bf174d256 [esp] Add ESP examples. 2025-02-01 14:33:40 -05:00
5f9f508581 [cpp] Remove ignores 2022-12-24 10:18:19 -05:00
3b6ecaa5e9 [cpp] Add Qt Desginer widget plugin examples 2022-12-24 10:16:30 -05:00
de652bad32 [cpp] Add catch and qt examples 2022-12-18 08:57:41 -05:00
d1fb33c58e [dotnet] Add dotnet projects and examples
+ Sitemap generator I created while learning the dispose pattern
+ Testing project for learning general C#
2022-05-04 14:59:17 -04:00
6dbac7559a [cpp] Update READMEs for C++ projects and examples 2022-05-04 12:54:06 -04:00
34f12250ab [cpp] Update weighted graph
+ totalWeight is now tracked for BFS & DFS traversals
+ Refactor graph search info structs
2022-04-14 14:37:53 -04:00
4b47630548 [cpp] Clean up graph implementations 2022-04-13 21:15:03 -04:00
6986c73651 [cpp] Add example of using condition_variables 2022-04-03 14:06:36 -04:00
92b3af7813 [cpp] Add example and solution for livelocks 2022-04-02 19:12:40 -04:00
6c0018c469 [cpp] Add example and solution for deadlocks 2022-04-02 12:05:06 -04:00
d81c65b1d2 [cpp] Add multithreaded project
+ Add example for race condition problem / solution
2022-04-02 11:40:58 -04:00
fc1f247987 [cpp] Add -Wall compiler option to root CMakeLists
+ Resolve all warnings
2022-03-31 17:42:23 -04:00
176 changed files with 18066 additions and 300 deletions

2
.gitignore vendored
View File

@@ -10,3 +10,5 @@
**/Makefile
**/*.cbp
**/node-modules/
**/CMakeLists.txt.user
**/catch2/bin/

View File

@@ -2,14 +2,16 @@
This repository is a collection of useful code snippets and configurations.
```
github.com/shaunrd0/klips/
```bash
shaunrd0/klips/
├── ansible # Ansible roles, playbooks, and examples
├── blockchain # Blockchain related project templates and examples
├── cpp # C++ programs, datastructures, and other examples
├── dotnet # .NET projects and examples
├── esp # ESP32 projects and examples
├── figlet # Figlet fonts I like :)
├── javascript # Javascript projects and examples
├── python # Python scripts or tools I've made
├── README.md
└── scripts # Bash scripts
├── python # Python scripts and tools I've made
├── scripts # Bash scripts
└── README.md
```

View File

@@ -1,4 +1,4 @@
# Ansible
# ansible
A few simple roles / plays I've put together in learning how to use Ansible can be found under their corresponding directories.

View File

@@ -1,3 +1,4 @@
# blockchain
A template project for getting started working on the Ethereum blockchain.
This project comes with basic packages for compiling and deploying Solidity contracts with Truffle.
@@ -72,7 +73,7 @@ I explain how to configure metamask on ropsten on [Knoats - Solidity](https://kn
Give yourself test Ethereum with the [Ropsten ETH Faucet](https://faucet.ropsten.be)
To deploy to ropsten test network, and verify using `truffle-verify-plugin` -
To deploy to ropsten test network, and verify using `truffle-verify-plugin` -
```asm
npx truffle migrate --network ropsten

View File

@@ -18,10 +18,14 @@ project(
)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
add_compile_options("-Wall")
add_subdirectory(algorithms)
add_subdirectory(catch2)
add_subdirectory(cmake-example)
add_subdirectory(cryptography)
add_subdirectory(datastructs)
add_subdirectory(graphics)
add_subdirectory(multithreading)
add_subdirectory(patterns)
add_subdirectory(qt)

View File

@@ -1,21 +1,23 @@
# Cpp
# cpp
```bash
shaunrd0/klips/cpp/
├── algorithms # Examples of various algorithms written in C++
├── cmake # Example of using cmake to build and organize larger projects
├── cryptography # Examples of encrypting / decrypting using ciphers in C++
├── datastructs # Collection of useful datastructures written in C++
├── graphics # Examples of graphics projects written in C++
├── patterns # Examples of various design patterns written in C++
├── algorithms # Examples of various algorithms written in C++
├── cmake # Example of using cmake to build and organize larger projects
├── cryptography # Examples of encrypting / decrypting using ciphers in C++
├── datastructs # Collection of useful datastructures written in C++
├── graphics # Examples of graphics projects written in C++
├── multithreading # Basic multithreading examples in C++
├── patterns # Examples of various design patterns written in C++
├── qt # Qt project examples using C++
└── README.md
```
This directory contains a `CMakeLists.txt`, which can be selected to open as a
This directory contains a `CMakeLists.txt`, which can be selected to open as a
project within your preferred IDE. From there, all nested examples can be built,
debugged, and ran.
Some of the more recent projects in this repository requires the latest CMake LTS.
Some of the more recent projects in this repository requires the latest CMake LTS.
To install `cmake` LTS with `apt` we can follow [official instructions from kitware](https://apt.kitware.com/)
Alternatively, we can install the LTS with python's `pip`.
```bash
@@ -32,7 +34,7 @@ cmake version 3.22.1
Once cmake is installed, dependencies for all examples can be installed with the command below.
```bash
sudo apt install libsdl2-dev freeglut3-dev
sudo apt install libsdl2-dev freeglut3-dev
```
If we build from this directory, we build all C++ projects and examples
@@ -61,7 +63,7 @@ graph-test-object sdl-test visitor-test
graph-test-simple select-sort
```
We can also build from subdirectories.
We can also build from subdirectories.
To only build projects related to design patterns we build from the `patterns/` subdirectory, for example
```bash
cd /path/to/klips/cpp/patterns
@@ -78,5 +80,5 @@ adapter-test factory-test prototype-test state-test
If cmake is not being used in a project, it can be built with `g++` manually using
the commands outlined in `*/.vscode/tasks.json`, or by using VSCode to open the example
and running the build task.
and running the build task.
Check the header comments in the main source file for the example for instructions.

View File

@@ -1,8 +1,7 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
## About: An example of an object graph implementation ##
## Algorithms in this example are found in MIT Intro to Algorithms ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Driver program to test object graph implementation ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################

View File

@@ -1,7 +1,8 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
## About: Driver program to test object graph implementation ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example of an object graph implementation ##
## Algorithms in this example are found in MIT Intro to Algorithms ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
@@ -183,4 +184,3 @@ std::vector<Node> Graph::TopologicalSort(const Node &startNode) const
// + Output is handled in main as FILO, similar to a stack
return order;
}

View File

@@ -1,6 +1,6 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example of an object graph implementation ##
## Algorithms in this example are found in MIT Intro to Algorithms ##
## ##
@@ -10,51 +10,14 @@
#ifndef LIB_GRAPH_HPP
#define LIB_GRAPH_HPP
#include <iostream>
#include <algorithm>
#include <iostream>
#include <map>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include <queue>
#include <unordered_set>
#include <unordered_map>
/******************************************************************************/
// Structures for tracking information gathered from various traversals
struct Node;
// Color represents the discovery status of any given node
// + White is undiscovered, Gray is in progress, Black is fully discovered
enum Color {White, Gray, Black};
// Information used in all searches
struct SearchInfo {
// Coloring of the nodes is used in both DFS and BFS
Color discovered = White;
};
// Information that is only used in BFS
struct BFS : SearchInfo {
// Used to represent distance from start node
int distance = 0;
// Used to represent the parent node that discovered this node
// + If we use this node as the starting point, this will remain a nullptr
const Node *predecessor = nullptr;
};
// Information that is only used in DFS
struct DFS : SearchInfo {
// Create a pair to track discovery / finish time
// + Discovery time is the iteration the node is first discovered
// + Finish time is the iteration the node has been checked completely
// ++ A finished node has considered all adjacent nodes
std::pair<int, int> discoveryFinish;
};
// Store search information in unordered_maps so we can pass it around easily
// + Allows each node to store relative information on the traversal
using InfoBFS = std::unordered_map<int, struct BFS>;
using InfoDFS = std::unordered_map<int, struct DFS>;
/******************************************************************************/
@@ -63,14 +26,16 @@ struct Node {
public:
// Constructors
Node(const Node &rhs) = default;
Node & operator=(Node rhs) {
Node & operator=(Node rhs)
{
if (this == &rhs) return *this;
swap(*this, rhs);
return *this;
}
Node(int num, std::vector<int> adj) : number(num), adjacent(std::move(adj)) {}
friend void swap(Node &a, Node &b) {
friend void swap(Node &a, Node &b)
{
std::swap(a.number, b.number);
std::swap(a.adjacent, b.adjacent);
}
@@ -85,8 +50,61 @@ public:
};
/******************************************************************************/
// Base struct for storing traversal information on all nodes
// Color represents the discovery status of any given node
enum Color {
// Node is marked as undiscovered
White,
// Node discovery is in progress; Some adjacent nodes have not been checked
Gray,
// Node has been discovered; All adjacent nodes have been checked
Black
};
// Information used in all searches tracked for each node
struct NodeInfo {
// Coloring of the nodes is used in both DFS and BFS
Color discovered = White;
};
/******************************************************************************/
// BFS search information struct
// Node information that is only used in BFS
struct BFS : NodeInfo {
// Used to represent distance from start node
int distance = 0;
// Used to represent the parent node that discovered this node
// + If we use this node as the starting point, this will remain a nullptr
const Node *predecessor = nullptr;
};
// Store search information in unordered_maps so we can pass it around easily
// + Allows each node to store relative information on the traversal
using InfoBFS = std::unordered_map<int, struct BFS>;
/******************************************************************************/
// DFS search information struct
// Node information that is only used in DFS
struct DFS : NodeInfo {
// Create a pair to track discovery / finish time
// + Discovery time is the iteration the node is first discovered
// + Finish time is the iteration the node has been checked completely
// ++ A finished node has considered all adjacent nodes
std::pair<int, int> discoveryFinish;
};
using InfoDFS = std::unordered_map<int, struct DFS>;
/******************************************************************************/
// Graph class declaration
class Graph {
public:
// Constructor
@@ -101,7 +119,7 @@ public:
// An alternate DFS that checks each node of the graph beginning at startNode
InfoDFS DFS(const Node &startNode) const;
// Visit function is used in both versions of DFS
void DFSVisit(int &time, const Node& startNode, InfoDFS &searchInfo) const;
void DFSVisit(int &time, const Node& startNode, InfoDFS &dfs) const;
// Topological sort, using DFS
std::vector<Node> TopologicalSort(const Node &startNode) const;

View File

@@ -1,6 +1,6 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Driver program to test a simple graph implementation ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##

View File

@@ -1,6 +1,6 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example of a simple graph implementation ##
## Algorithms in this example are found in MIT Intro to Algorithms ##
## ##

View File

@@ -1,6 +1,6 @@
/*#############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example of a simple graph implementation ##
## Algorithms in this example are found in MIT Intro to Algorithms ##
## ##
@@ -12,9 +12,9 @@
#include <iostream>
#include <queue>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <vector>
class Graph {

View File

@@ -1,7 +1,7 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
## About: An example of a weighted graph implementation ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Driver program to test templated object graph implementation ##
## Algorithms in this example are found in MIT Intro to Algorithms ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##

View File

@@ -1,7 +1,7 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
## About: An example of an object graph implementation ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example of a templated object graph implementation ##
## Algorithms in this example are found in MIT Intro to Algorithms ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
@@ -10,38 +10,88 @@
#ifndef LIB_GRAPH_HPP
#define LIB_GRAPH_HPP
#include <iostream>
#include <algorithm>
#include <iostream>
#include <map>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include <queue>
#include <unordered_set>
#include <unordered_map>
/******************************************************************************/
// Node structure for representing a graph
template <typename T>
struct Node {
public:
template <typename> friend class Graph;
template <typename> friend class InfoMST;
// Constructors
Node(const Node &rhs) = default;
Node & operator=(Node rhs)
{
if (this == &rhs) return *this;
swap(*this, rhs);
return *this;
}
Node(T data, const std::vector<std::pair<T, int>> &adj) : data_(data)
{
// Place each adjacent node in vector into our unordered_map of edges
for (const auto &i : adj) adjacent_.emplace(i.first, i.second);
}
friend void swap(Node &a, Node &b)
{
std::swap(a.data_, b.data_);
std::swap(a.adjacent_, b.adjacent_);
}
// Operators
// Define operator== for std::find; And comparisons between nodes
bool operator==(const Node<T> &b) const { return this->data_ == b.data_;}
// Define an operator!= for comparing nodes for inequality
bool operator!=(const Node<T> &b) const { return this->data_ != b.data_;}
// Accessors
inline T GetData() const { return data_;}
inline std::unordered_map<int, int> GetAdjacent() const { return adjacent_;}
private:
T data_;
// Adjacent stored in an unordered_map<adj.number, edgeWeight>
std::unordered_map<T, int> adjacent_;
};
/******************************************************************************/
// Base struct for storing traversal information on all nodes
template <typename T> struct Node;
// Color represents the discovery status of any given node
// + White is undiscovered, Gray is in progress, Black is fully discovered
enum Color {White, Gray, Black};
enum Color {
// Node is marked as undiscovered
White,
// Node discovery is in progress; Some adjacent nodes have not been checked
Gray,
// Node has been discovered; All adjacent nodes have been checked
Black
};
// Information used in all searches
struct SearchInfo {
// Information used in all searches tracked for each node
struct NodeInfo {
// Coloring of the nodes is used in both DFS and BFS
Color discovered = White;
};
/******************************************************************************/
// BFS search information structs
// BFS search information struct
// Information that is only used in BFS
// Node information that is only used in BFS
template <typename T>
struct BFS : SearchInfo {
struct BFS : NodeInfo {
// Used to represent distance from start node
int distance = 0;
// Used to represent the parent node that discovered this node
@@ -49,12 +99,16 @@ struct BFS : SearchInfo {
const Node<T> *predecessor = nullptr;
};
// Store search information in unordered_maps so we can pass it around easily
// + Allows each node to store relative information on the traversal
template <typename T> using InfoBFS = std::unordered_map<T, struct BFS<T>>;
/******************************************************************************/
// DFS search information structs
// DFS search information struct
// Information that is only used in DFS
struct DFS : SearchInfo {
// Node information that is only used in DFS
struct DFS : NodeInfo {
// Create a pair to track discovery / finish time
// + Discovery time is the iteration the node is first discovered
// + Finish time is the iteration the node has been checked completely
@@ -62,22 +116,16 @@ struct DFS : SearchInfo {
std::pair<int, int> discoveryFinish;
};
template <typename T> using InfoDFS = std::unordered_map<T, struct DFS>;
/******************************************************************************/
// Alias types for storing search information structures
// MST search information struct
// Store search information in unordered_maps so we can pass it around easily
// + Allows each node to store relative information on the traversal
template <typename T> using InfoBFS = std::unordered_map<T, struct BFS<T>>;
template <typename T> using InfoDFS = std::unordered_map<T, struct DFS>;
// Edges stored as multimap<weight, pair<nodeA.data_, nodeB.data_>>
template <typename T> using Edges = std::multimap<int, std::pair<T, T>>;
/******************************************************************************/
// MST search information structs
struct MST : SearchInfo {
struct MST : NodeInfo {
int32_t parent = INT32_MIN;
int rank = 0;
};
@@ -86,8 +134,9 @@ template <typename T>
struct InfoMST {
template <typename> friend class Graph;
explicit InfoMST(const std::vector<Node<T>> &nodes) {
for (const auto &node : nodes){
explicit InfoMST(const std::vector<Node<T>> &nodes)
{
for (const auto &node : nodes) {
// Initialize the default values for forest tracked by this struct
// + This data is used in KruskalMST() to find the MST
MakeSet(node.data_);
@@ -157,51 +206,6 @@ private:
};
/******************************************************************************/
// Node structure for representing a graph
template <typename T>
struct Node {
public:
template <typename> friend class Graph;
template <typename> friend class InfoMST;
// Constructors
Node(const Node &rhs) = default;
Node & operator=(Node rhs) {
if (this == &rhs) return *this;
swap(*this, rhs);
return *this;
}
Node(T data, const std::vector<std::pair<T, int>> &adj)
: data_(data)
{
// Place each adjacent node in vector into our unordered_map of edges
for (const auto &i : adj) adjacent_.emplace(i.first, i.second);
}
friend void swap(Node &a, Node &b) {
std::swap(a.data_, b.data_);
std::swap(a.adjacent_, b.adjacent_);
}
// Operators
// Define operator== for std::find; And comparisons between nodes
bool operator==(const Node<T> &b) const { return this->data_ == b.data_;}
// Define an operator!= for comparing nodes for inequality
bool operator!=(const Node<T> &b) const { return this->data_ != b.data_;}
// Accessors
inline T GetData() const { return data_;}
inline std::unordered_map<int, int> GetAdjacent() const { return adjacent_;}
private:
T data_;
// Adjacent stored in an unordered_map<adj.number, edgeWeight>
std::unordered_map<T, int> adjacent_;
};
/******************************************************************************/
// Templated graph class
@@ -209,7 +213,7 @@ template <class T>
class Graph {
public:
// Constructor
Graph(std::vector<Node<T>> nodes) : nodes_(std::move(nodes)) {}
explicit Graph(std::vector<Node<T>> nodes) : nodes_(std::move(nodes)) {}
// Breadth First Search
InfoBFS<T> BFS(const Node<T>& startNode) const;

View File

@@ -1,8 +1,7 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
## About: An example of a weighted graph implementation ##
## Algorithms in this example are found in MIT Intro to Algorithms ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Driver program to test weighted graph implementation ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
@@ -105,7 +104,7 @@ int main (const int argc, const char * argv[])
// + Chapter 22, Figure 22.4 on DFS
// Unlike the simple-graph example, this final result matches MIT Algorithms
// + Aside from the placement of the watch node, which is not connected
// + This is because the node is visited after all other nodes are finished
// + This is because the node is visited after all other nodes are finished
std::vector<Node> order =
topologicalGraph.TopologicalSort(topologicalGraph.GetNodeCopy(6));
std::cout << "\nTopological order: ";
@@ -157,11 +156,19 @@ int main (const int argc, const char * argv[])
{9, {{3, 2}, {7, 6}}}
}
);
std::cout << "\nChecking weight traversing graph from node 1 using DFS...\n";
InfoDFS resultDFS = graphMST.DFS(graphMST.GetNodeCopy(1));
std::cout << "DFS total weight traversed: " << resultDFS.totalWeight << std::endl;
std::cout << "\nChecking weight traversing graph from node 1 using BFS...\n";
InfoBFS resultBFS = graphMST.BFS(graphMST.GetNodeCopy(1));
std::cout << "BFS total weight traversed: " << resultBFS.totalWeight << std::endl;
InfoMST resultMST = graphMST.KruskalMST();
std::cout << "Finding MST using Kruskal's...\n\nMST result: \n";
std::cout << "\n\nFinding MST using Kruskal's...\n\nMST result: \n";
for (const auto &edge : resultMST.edgesMST) {
std::cout << "Connected nodes: " << edge.second.first << "->"
<< edge.second.second << " with weight of " << edge.first << "\n";
}
std::cout << "Total MST weight: " << resultMST.weightMST << std::endl;
std::cout << "Total MST weight: " << resultMST.totalWeight << std::endl;
}

View File

@@ -1,7 +1,8 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
## About: Driver program to test object graph implementation ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example of a weighted graph implementation ##
## Algorithms in this example are found in MIT Intro to Algorithms ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
@@ -13,13 +14,13 @@
InfoBFS Graph::BFS(const Node& startNode) const
{
// Create local object to track the information gathered during traversal
InfoBFS searchInfo;
InfoBFS bfs;
// Create a queue to visit discovered nodes in FIFO order
std::queue<const Node *> visitQueue;
// Mark the startNode as in progress until we finish checking adjacent nodes
searchInfo[startNode.number].discovered = Gray;
bfs.nodeInfo[startNode.number].discovered = Gray;
// Visit the startNode
visitQueue.push(&startNode);
@@ -30,17 +31,17 @@ InfoBFS Graph::BFS(const Node& startNode) const
const Node * thisNode = visitQueue.front();
visitQueue.pop();
std::cout << "Visiting node " << thisNode->number << std::endl;
// Check if we have already discovered all the adjacentNodes to thisNode
for (const auto &adjacent : thisNode->adjacent) {
if (searchInfo[adjacent.first].discovered == White) {
if (bfs.nodeInfo[adjacent.first].discovered == White) {
std::cout << "Found undiscovered adjacentNode: " << adjacent.first
<< "\n";
<< " with weight of " << adjacent.second << std::endl;
bfs.totalWeight += adjacent.second;
// Mark the adjacent node as in progress
searchInfo[adjacent.first].discovered = Gray;
searchInfo[adjacent.first].distance =
searchInfo[thisNode->number].distance + 1;
searchInfo[adjacent.first].predecessor =
bfs.nodeInfo[adjacent.first].discovered = Gray;
bfs.nodeInfo[adjacent.first].distance =
bfs.nodeInfo[thisNode->number].distance + 1;
bfs.nodeInfo[adjacent.first].predecessor =
&GetNode(thisNode->number);
// Add the discovered node the the visitQueue
@@ -48,11 +49,11 @@ InfoBFS Graph::BFS(const Node& startNode) const
}
}
// We are finished with this node and the adjacent nodes; Mark it discovered
searchInfo[thisNode->number].discovered = Black;
bfs.nodeInfo[thisNode->number].discovered = Black;
}
// Return the information gathered from this search, JIC caller needs it
return searchInfo;
return bfs;
}
std::deque<Node> Graph::PathBFS(const Node &start, const Node &finish) const
@@ -61,8 +62,8 @@ std::deque<Node> Graph::PathBFS(const Node &start, const Node &finish) const
// + If the caller modifies these, it will not impact the graph's data
std::deque<Node> path;
InfoBFS searchInfo = BFS(start);
const Node * next = searchInfo[finish.number].predecessor;
InfoBFS bfs = BFS(start);
const Node * next = bfs.nodeInfo[finish.number].predecessor;
bool isValid = false;
do {
// If we have reached the start node, we have found a valid path
@@ -73,7 +74,7 @@ std::deque<Node> Graph::PathBFS(const Node &start, const Node &finish) const
path.emplace_front(*next);
// Move to the next node
next = searchInfo[next->number].predecessor;
next = bfs.nodeInfo[next->number].predecessor;
} while (next != nullptr);
// Use emplace_back to call Node copy constructor
path.emplace_back(finish);
@@ -88,85 +89,83 @@ std::deque<Node> Graph::PathBFS(const Node &start, const Node &finish) const
InfoDFS Graph::DFS() const
{
// Track the nodes we have discovered
InfoDFS searchInfo;
InfoDFS dfs;
int time = 0;
// Visit each node in the graph
for (const auto& node : nodes_) {
for (const auto & node : nodes_) {
std::cout << "Visiting node " << node.number << std::endl;
// If the node is undiscovered, visit it
if (searchInfo[node.number].discovered == White) {
if (dfs.nodeInfo[node.number].discovered == White) {
std::cout << "Found undiscovered node: " << node.number << std::endl;
// Visiting the undiscovered node will check it's adjacent nodes
DFSVisit(time, node, searchInfo);
DFSVisit(time, node, dfs);
}
}
return searchInfo;
return dfs;
}
InfoDFS Graph::DFS(const Node &startNode) const
{
// Track the nodes we have discovered
InfoDFS searchInfo;
InfoDFS dfs;
int time = 0;
auto startIter = std::find(nodes_.begin(), nodes_.end(),
Node(startNode.number, {})
);
auto startIter =
std::find(nodes_.begin(), nodes_.end(), Node(startNode.number, { }));
// beginning at startNode, visit each node in the graph until we reach the end
while (startIter != nodes_.end()) {
std::cout << "Visiting node " << startIter->number << std::endl;
// If the startIter is undiscovered, visit it
if (searchInfo[startIter->number].discovered == White) {
std::cout << "Found undiscovered node: " << startIter->number << std::endl;
if (dfs.nodeInfo[startIter->number].discovered == White) {
std::cout << "Found undiscovered node: " << startIter->number
<< std::endl;
// Visiting the undiscovered node will check it's adjacent nodes
DFSVisit(time, *startIter, searchInfo);
DFSVisit(time, *startIter, dfs);
}
startIter++;
}
// Once we reach the last node, check the beginning for unchecked nodes
startIter = nodes_.begin();
// Once we reach the initial startNode, we have checked all nodes
while (*startIter != startNode) {
std::cout << "Visiting node " << startIter->number << std::endl;
// If the startIter is undiscovered, visit it
if (searchInfo[startIter->number].discovered == White) {
if (dfs.nodeInfo[startIter->number].discovered == White) {
std::cout << "Found undiscovered node: " << startIter->number << std::endl;
// Visiting the undiscovered node will check it's adjacent nodes
DFSVisit(time, *startIter, searchInfo);
DFSVisit(time, *startIter, dfs);
}
startIter++;
}
return searchInfo;
return dfs;
}
void Graph::DFSVisit(int &time, const Node& startNode, InfoDFS &searchInfo) const
void Graph::DFSVisit(int &time, const Node& startNode, InfoDFS &dfs) const
{
searchInfo[startNode.number].discovered = Gray;
dfs.nodeInfo[startNode.number].discovered = Gray;
time++;
searchInfo[startNode.number].discoveryFinish.first = time;
dfs.nodeInfo[startNode.number].discoveryFinish.first = time;
// Check the adjacent nodes of the startNode
for (const auto &adjacent : startNode.adjacent) {
auto iter = std::find(nodes_.begin(), nodes_.end(),
Node(adjacent.first, {}));
for (const auto & adjacent : startNode.adjacent) {
const auto node = GetNode(adjacent.first);
// If the adjacentNode is undiscovered, visit it
// + Offset by 1 to account for 0 index of discovered vector
if (searchInfo[iter->number].discovered == White) {
std::cout << "Found undiscovered adjacentNode: "
<< GetNode(adjacent.first).number << std::endl;
if (dfs.nodeInfo[node.number].discovered == White) {
std::cout << "Found undiscovered adjacentNode: " << adjacent.first
<< " with weight of " << adjacent.second << std::endl;
// Visiting the undiscovered node will check it's adjacent nodes
DFSVisit(time, *iter, searchInfo);
dfs.totalWeight += adjacent.second;
DFSVisit(time, node, dfs);
}
}
searchInfo[startNode.number].discovered = Black;
dfs.nodeInfo[startNode.number].discovered = Black;
time++;
searchInfo[startNode.number].discoveryFinish.second = time;
dfs.nodeInfo[startNode.number].discoveryFinish.second = time;
}
std::vector<Node> Graph::TopologicalSort(const Node &startNode) const
@@ -176,8 +175,8 @@ std::vector<Node> Graph::TopologicalSort(const Node &startNode) const
std::vector<Node> order(nodes_);
auto comp = [&topological](const Node &a, const Node &b) {
return (topological[a.number].discoveryFinish.second <
topological[b.number].discoveryFinish.second);
return (topological.nodeInfo[a.number].discoveryFinish.second <
topological.nodeInfo[b.number].discoveryFinish.second);
};
std::sort(order.begin(), order.end(), comp);
@@ -189,27 +188,26 @@ std::vector<Node> Graph::TopologicalSort(const Node &startNode) const
InfoMST Graph::KruskalMST() const
{
InfoMST searchInfo(nodes_);
InfoMST mst(nodes_);
// The ctor for InfoMST initializes all edges within the graph into a multimap
// + Key for multimap is edge weight, so they're already sorted in ascending
// For each edge in the graph, check if they are part of the same tree
// + Since we do not want to create a cycle in the MST forest -
// + we don't connect nodes that are part of the same tree
for (const auto &edge : searchInfo.edges) {
for (const auto &edge : mst.edges) {
// Two integers representing the node.number for the connected nodes
const int u = edge.second.first;
const int v = edge.second.second;
// Check if the nodes are of the same tree
if (searchInfo.FindSet(u) != searchInfo.FindSet(v)) {
if (mst.FindSet(u) != mst.FindSet(v)) {
// If they are not, add the edge to our MST
searchInfo.edgesMST.emplace(edge);
searchInfo.weightMST += edge.first;
mst.edgesMST.emplace(edge);
mst.totalWeight += edge.first;
// Update the forest to reflect this change
searchInfo.Union(u, v);
mst.Union(u, v);
}
}
return searchInfo;
return mst;
}

View File

@@ -1,7 +1,7 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
## About: An example of an object graph implementation ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example of a weighted graph implementation ##
## Algorithms in this example are found in MIT Intro to Algorithms ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
@@ -10,56 +10,14 @@
#ifndef LIB_GRAPH_HPP
#define LIB_GRAPH_HPP
#include <iostream>
#include <algorithm>
#include <iostream>
#include <map>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include <queue>
#include <unordered_set>
#include <unordered_map>
/******************************************************************************/
// Structures for tracking information gathered from various traversals
struct Node;
// Color represents the discovery status of any given node
// + White is undiscovered, Gray is in progress, Black is fully discovered
enum Color {White, Gray, Black};
// Information used in all searches
struct SearchInfo {
// Coloring of the nodes is used in both DFS and BFS
Color discovered = White;
};
// Information that is only used in BFS
struct BFS : SearchInfo {
// Used to represent distance from start node
int distance = 0;
// Used to represent the parent node that discovered this node
// + If we use this node as the starting point, this will remain a nullptr
const Node *predecessor = nullptr;
};
// Information that is only used in DFS
struct DFS : SearchInfo {
// Create a pair to track discovery / finish time
// + Discovery time is the iteration the node is first discovered
// + Finish time is the iteration the node has been checked completely
// ++ A finished node has considered all adjacent nodes
std::pair<int, int> discoveryFinish;
};
struct MST : SearchInfo {
int32_t parent = INT32_MIN;
int rank = 0;
};
// Store search information in unordered_maps so we can pass it around easily
// + Allows each node to store relative information on the traversal
using InfoBFS = std::unordered_map<int, struct BFS>;
using InfoDFS = std::unordered_map<int, struct DFS>;
/******************************************************************************/
@@ -69,7 +27,8 @@ struct Node {
public:
// Constructors
Node(const Node &rhs) = default;
Node & operator=(Node rhs) {
Node & operator=(Node rhs)
{
if (this == &rhs) return *this;
swap(*this, rhs);
return *this;
@@ -80,7 +39,8 @@ public:
for (const auto &i : adj) adjacent.emplace(i.first, i.second);
}
friend void swap(Node &a, Node &b) {
friend void swap(Node &a, Node &b)
{
std::swap(a.number, b.number);
std::swap(a.adjacent, b.adjacent);
}
@@ -95,10 +55,81 @@ public:
bool operator!=(const Node &b) const { return this->number != b.number;}
};
/******************************************************************************/
// Base struct for storing traversal information on all nodes
// Color represents the discovery status of any given node
enum Color {
// Node is marked as undiscovered
White,
// Node discovery is in progress; Some adjacent nodes have not been checked
Gray,
// Node has been discovered; All adjacent nodes have been checked
Black
};
// Information used in all searches tracked for each node
struct NodeInfo {
// Coloring of the nodes is used in both DFS and BFS
Color discovered = White;
};
// Template for tracking graph information gathered during traversals
// + Used for DFS, BFS, and MST
template <typename T>
struct GraphInfo {
// Store search information in unordered_maps so we can pass it around easily
// + Allows each node to store relative information on the traversal
std::unordered_map<int, T> nodeInfo;
// Track total weight for all traversals
int totalWeight = 0;
};
/******************************************************************************/
// BFS search information struct
// Node search information that is only used in BFS
struct BFS : NodeInfo {
// Used to represent distance from start node
int distance = 0;
// Used to represent the parent node that discovered this node
// + If we use this node as the starting point, this will remain a nullptr
const Node *predecessor = nullptr;
};
struct InfoBFS : GraphInfo<BFS> {/* Members inherited from GraphInfo */};
/******************************************************************************/
// DFS search information struct
// Information that is only used in DFS
struct DFS : NodeInfo {
// Create a pair to track discovery / finish time
// + Discovery time is the iteration the node is first discovered
// + Finish time is the iteration the node has been checked completely
// ++ A finished node has considered all adjacent nodes
std::pair<int, int> discoveryFinish;
};
struct InfoDFS : GraphInfo<DFS> {/* Members inherited from GraphInfo */};
/******************************************************************************/
// MST search information struct
struct MST : NodeInfo {
int32_t parent = INT32_MIN;
int rank = 0;
};
using Edges = std::multimap<int, std::pair<int, int>>;
struct InfoMST {
explicit InfoMST(const std::vector<Node> &nodes) {
for (const auto &node : nodes){
struct InfoMST : GraphInfo<MST>{
explicit InfoMST(const std::vector<Node> &nodes)
{
for (const auto &node : nodes) {
// Initialize the default values for forest tracked by this struct
// + This data is used in KruskalMST() to find the MST
MakeSet(node.number);
@@ -113,20 +144,17 @@ struct InfoMST {
}
}
std::unordered_map<int, struct MST> searchInfo;
// All of the edges within our graph
// + Since each node stores its own edges, this is initialized in InfoMST ctor
Edges edges;
// A multimap of the edges found for our MST
Edges edgesMST;
// The total weight of our resulting MST
int weightMST = 0;
void MakeSet(int x)
{
searchInfo[x].parent = x;
searchInfo[x].rank = 0;
nodeInfo[x].parent = x;
nodeInfo[x].rank = 0;
}
void Union(int x, int y)
@@ -136,29 +164,30 @@ struct InfoMST {
void Link(int x, int y)
{
if (searchInfo[x].rank > searchInfo[y].rank) {
searchInfo[y].parent = x;
if (nodeInfo[x].rank > nodeInfo[y].rank) {
nodeInfo[y].parent = x;
}
else {
searchInfo[x].parent = y;
if (searchInfo[x].rank == searchInfo[y].rank) {
searchInfo[y].rank += 1;
nodeInfo[x].parent = y;
if (nodeInfo[x].rank == nodeInfo[y].rank) {
nodeInfo[y].rank += 1;
}
}
}
int FindSet(int x)
{
if (x != searchInfo[x].parent) {
searchInfo[x].parent = FindSet(searchInfo[x].parent);
if (x != nodeInfo[x].parent) {
nodeInfo[x].parent = FindSet(nodeInfo[x].parent);
}
return searchInfo[x].parent;
return nodeInfo[x].parent;
}
};
/******************************************************************************/
// Graph class declaration
class Graph {
public:
// Constructor
@@ -173,7 +202,7 @@ public:
// An alternate DFS that checks each node of the graph beginning at startNode
InfoDFS DFS(const Node &startNode) const;
// Visit function is used in both versions of DFS
void DFSVisit(int &time, const Node& startNode, InfoDFS &searchInfo) const;
void DFSVisit(int &time, const Node& startNode, InfoDFS &dfs) const;
// Topological sort, using DFS
std::vector<Node> TopologicalSort(const Node &startNode) const;
// Kruskal's MST

View File

@@ -15,9 +15,9 @@
void BubbleSort(std::vector<int> &array)
{
// For each value within the set, starting at 0
for (int sortedPivot = 0; sortedPivot < array.size(); sortedPivot++) {
for (size_t sortedPivot = 0; sortedPivot < array.size(); sortedPivot++) {
// Check every other remaining value in the set
for (int j = array.size() - 1; j > sortedPivot; j--) {
for (size_t j = array.size() - 1; j > sortedPivot; j--) {
// Swap if the value at j is less than the value before it
if (array[j] < array[j - 1]) {
std::swap(array[j], array[j - 1]);

View File

@@ -33,7 +33,7 @@ void CountingSort(std::vector<int> &array)
// Count the values less than or equal to each element of tempArray
// + Since each element stores its own count, just add the count at index i-1
for (size_t i = 1; i <= maxValue; i++) {
for (int32_t i = 1; i <= maxValue; i++) {
tempArray[i] += tempArray[i - 1];
// tempArray[i] - 1 now represents the sorted 0-index pos for each value i
}

View File

@@ -17,7 +17,7 @@ size_t Parent(const size_t &index) { return index / 2;}
size_t Left(const size_t &index) { return 2 * index + 1;}
size_t Right(const size_t &index) { return (2 * index) + 2;}
void MaxHeapify(std::vector<int> &array, size_t thisIndex, const int &heapSize)
void MaxHeapify(std::vector<int> &array, size_t thisIndex, const size_t &heapSize)
{
// Get an index for the left and right nodes attached to thisIndex
size_t l = Left(thisIndex);

View File

@@ -18,7 +18,7 @@ size_t Parent(const size_t &index);
size_t Left(const size_t &index);
size_t Right(const size_t &index);
void MaxHeapify(std::vector<int> &array, size_t thisIndex, const int &heapSize);
void MaxHeapify(std::vector<int> &array, size_t thisIndex, const size_t &heapSize);
void BuildMaxHeap(std::vector<int> &array);

View File

@@ -15,7 +15,7 @@ void InsertionSort(std::vector<int> &array)
{
// For each value, move left until we find sortedPosition for keyValue
// + Starting with keyValue at array[1], to check sortedPosition at array[0]
for (int keyIndex = 1; keyIndex <= array.size(); keyIndex++) {
for (size_t keyIndex = 1; keyIndex <= array.size(); keyIndex++) {
// Save the current key value
// + We will look for the sorted position of this value
const int keyValue = array[keyIndex];

View File

@@ -50,7 +50,7 @@ size_t Partition(std::vector<int> &array, size_t begin, size_t end)
// + Return this value when done, so we know where the lhs partition ends
ssize_t lhsIndex = begin - 1;
// For each value within this partition, check for values < keyValue
for (int j = begin; j <= end - 1; j++) {
for (size_t j = begin; j <= end - 1; j++) {
if (array[j] <= keyValue) {
// Swap all values < keyValue into the lhs portion of array
std::swap(array[++lhsIndex], array[j]);

View File

@@ -41,7 +41,7 @@ void CountingSort(std::vector<int> &array, int placeValue)
// Count the values less than or equal to each element of tempArray
// + Since each element stores its own count, just add the count at index i-1
for (int i = 1; i < tempArray.size(); i++) {
for (size_t i = 1; i < tempArray.size(); i++) {
tempArray[i] = tempArray[i] + tempArray[i - 1];
}

View File

@@ -12,10 +12,10 @@
#include <vector>
void SelectionSort(std::vector<int> &arr) {
for (int leftIndex = 0; leftIndex < arr.size(); leftIndex++) {
for (size_t leftIndex = 0; leftIndex < arr.size(); leftIndex++) {
// Get the index for the minimum number in the unsorted set
int min = leftIndex;
for (int i = leftIndex; i < arr.size(); i++) {
size_t min = leftIndex;
for (size_t i = leftIndex; i < arr.size(); i++) {
// Check if value at i is smaller than value at min index
min = (arr[min] > arr[i]) ? i : min; // Update min value to i if true
}

31
cpp/catch2/CMakeLists.txt Normal file
View File

@@ -0,0 +1,31 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Practice project for testing with catch2 framework ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
cmake_minimum_required(VERSION 3.15)
project(
#[[NAME]] Catch2
VERSION 1.0
DESCRIPTION "Practice project for learning Catch2"
LANGUAGES CXX
)
add_compile_options(-Wall)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
Include(FetchContent)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.0.1
)
FetchContent_MakeAvailable(Catch2)
add_subdirectory(src)
add_subdirectory(test)

View File

@@ -0,0 +1,10 @@
#ifndef KLIPS_KLIPS_H
#define KLIPS_KLIPS_H
class klips { };
unsigned int factorial(unsigned int);
#endif // KLIPS_KLIPS_H

View File

@@ -0,0 +1,29 @@
// Authored by 康桓瑋 on SO: https://stackoverflow.com/a/56766138
#ifndef CATCH2_TYPE_NAME_HPP
#include <string_view>
template <typename T>
constexpr auto type_name() {
std::string_view name, prefix, suffix;
#ifdef __clang__
name = __PRETTY_FUNCTION__;
prefix = "auto type_name() [T = ";
suffix = "]";
#elif defined(__GNUC__)
name = __PRETTY_FUNCTION__;
prefix = "constexpr auto type_name() [with T = ";
suffix = "]";
#elif defined(_MSC_VER)
name = __FUNCSIG__;
prefix = "auto __cdecl type_name<";
suffix = ">(void)";
#endif
name.remove_prefix(prefix.size());
name.remove_suffix(suffix.size());
return name;
}
#define CATCH2_TYPE_NAME_HPP
#endif // CATCH2_TYPE_NAME_HPP

View File

@@ -0,0 +1,22 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Practice project for testing with catch2 framework ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
cmake_minimum_required(VERSION 3.15)
project(
#[[NAME]] Catch2
VERSION 1.0
DESCRIPTION "Practice project for learning Catch2"
LANGUAGES CXX
)
add_compile_options(-Wall)
add_definitions("-std=c++17")
add_library(klips SHARED klips.cpp)
target_include_directories(klips PRIVATE ${CMAKE_SOURCE_DIR}/include)

4
cpp/catch2/src/klips.cpp Normal file
View File

@@ -0,0 +1,4 @@
unsigned int factorial( unsigned int number ) {
return number <= 1 ? number : factorial(number-1)*number;
}

View File

@@ -0,0 +1,22 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Practice project for testing with catch2 framework ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
cmake_minimum_required(VERSION 3.15)
project(
#[[NAME]] Catch2
VERSION 1.0
DESCRIPTION "Practice project for learning Catch2"
LANGUAGES CXX
)
add_compile_options(-Wall)
add_executable(test_klips test_klips.cpp)
target_link_libraries(test_klips PRIVATE Catch2::Catch2WithMain klips)
target_include_directories(test_klips PRIVATE ${CMAKE_SOURCE_DIR}/include)

View File

@@ -0,0 +1,147 @@
#define CATCH_CONFIG_MAIN
#include <iostream>
#include "../bin/catch.hpp"
#include "klips.hpp"
#include "type_name.hpp"
#define TT() std::cout << "T = " << type_name<TestType>() << std::endl;
#define TD(x) \
std::cout << type_name<decltype(x)>() << " " << #x << " = " << x << std::endl;
#define T(x) std::cout << "T = " << type_name<x>() << std::endl;
TEST_CASE("factorials are computed", "[factorial]") {
REQUIRE(factorial(1) == 1);
REQUIRE(factorial(2) == 2);
REQUIRE(factorial(3) == 6);
REQUIRE(factorial(10) == 3628800);
}
TEST_CASE("Generators") {
auto i = GENERATE(1, 3, 5);
TD(i);
}
TEST_CASE("Generators 2") {
auto i = GENERATE(1, 2);
SECTION("one") {
auto j = GENERATE(-3, -2);
REQUIRE(j < i);
std::cout << "i = " << i << "; j = " << j << std::endl;
}
SECTION("two") {
auto k = GENERATE(4, 5, 6);
REQUIRE(i != k);
std::cout << "i = " << i << "; k = " << k << std::endl;
}
}
TEST_CASE("Complex mix of sections and generates") {
auto i = GENERATE(1, 2);
SECTION("A") {
std::cout << "i = " << i << "; A passed" << std::endl;
SUCCEED("A");
}
std::cout << "left A\n";
auto j = GENERATE(3, 4);
std::cout << "i = " << i << "; j = " << j << std::endl;
SECTION("B") {
std::cout << "i = " << i << "; j = " << j << "; B passed;" << std::endl;
SUCCEED("B");
}
auto k = GENERATE(5, 6);
std::cout << "i = " << i << "; k = " << k << std::endl;
SUCCEED();
}
TEST_CASE("Test generaators 3", "[test]") { GENERATE(values({1, 2})); }
TEMPLATE_TEST_CASE("Testing template tests", "[test][template]", int8_t,
int16_t, int32_t, int64_t) {
TT();
}
template <typename T> struct Foo {
size_t size() { return 0; }
};
template <typename T> struct Test {
T test() {
T x;
return x;
}
};
TEMPLATE_PRODUCT_TEST_CASE("A Template product test case",
"[template][product]", (std::vector, Test),
(int, float)) {
TT();
}
TEMPLATE_PRODUCT_TEST_CASE("Product with differing arities",
"[template][product]", std::tuple,
(int, (int, double), (int, double, float))) {
TT();
}
using types = std::tuple<int8_t, int16_t, int32_t, int64_t>;
TEMPLATE_LIST_TEST_CASE("Testing template list tests", "[test][template][list]",
types) {
TT();
}
TEMPLATE_TEST_CASE_SIG(
"TemplateTestSig: arrays can be created from NTTP arguments",
"[vector][template][nttp]", ((typename T, int V), T, V), (int, 5),
(float, 4), (std::string, 15), ((std::tuple<int, float>), 6)) {
T(T);
std::cout << "V = " << V;
std::array<T, V> v;
REQUIRE(v.size() > 1);
}
template <typename T, size_t S> struct Bar {
size_t size() { return S; }
};
TEMPLATE_PRODUCT_TEST_CASE_SIG(
"A Template product test case with array signature",
"[template][product][nttp]", ((typename T, size_t S), T, S),
(std::array, Bar), ((int, 9), (float, 42))) {
TT();
TestType x;
REQUIRE(x.size() > 0);
}
template <typename T> struct test_config_get {
template <bool must_find> void run() {
// Config c{};
// std::string key{"the_key"};
// std::string value{"the_value"};
// c.set(key, value);
if constexpr (must_find) {
std::cout << "Test 1 ran";
} else {
std::cout << "Test 2 ran";
}
}
};
template <> template <bool must_find> void test_config_get<std::string>::run() {
if constexpr (must_find) {
std::cout << "Test 1 ran for strings";
} else {
std::cout << "Test 2 ran for strings";
}
}
TEMPLATE_PRODUCT_TEST_CASE("Test", "[test]", test_config_get,
(int, std::string)) {
TT();
TestType t;
test_config_get<int> s;
s.template run<true>();
// TestType t;
// t.run<true>();
}

View File

@@ -21,9 +21,9 @@ void Columnar::InitOrder(std::string temp)
temp.erase(it, temp.end());
// Step through each character in lexicographic order
for (int i = 0; i < temp.size(); i++) {
for (size_t i = 0; i < temp.size(); i++) {
// Check each character in the keyWord for the current lexicographic char
for (int j = 0; j < keyWord_.size(); j++) {
for (size_t j = 0; j < keyWord_.size(); j++) {
// If they are equal, push the index of the char in keyWord to orderVect
if (keyWord_[j] == temp[i]) {
orderVect_.push_back(j);
@@ -109,7 +109,7 @@ std::string Columnar::Decrypt(std::string message)
rows.resize(orderVect_.size());
// Track the ending position after each substring is taken
int lastPos = 0;
for (int i = 0; i < orderVect_.size(); i++) {
for (size_t i = 0; i < orderVect_.size(); i++) {
// If we are assigning to any row < fullRows, it should have + 1 character
if (orderVect_[i] < fullRows) {
rows[orderVect_[i]] = message.substr(lastPos, rowLength + 1);

View File

@@ -0,0 +1,24 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: A root project for practicing C++ multithreading ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
cmake_minimum_required(VERSION 3.16)
project(
#[[NAME]] Multithreading
VERSION 1.0
DESCRIPTION "Practice with multithreaded programming in C++"
LANGUAGES CXX
)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
add_compile_options("-Wall")
add_subdirectory(conditions)
add_subdirectory(deadlock)
add_subdirectory(livelock)
add_subdirectory(race-condition)

View File

@@ -0,0 +1,25 @@
# Multithreading
A few basic multithreading programs written in C++ while learning about
the [concurrency support library](https://en.cppreference.com/w/cpp/thread)
```
klips/cpp/multithreading
.
├── conditions # Using condition_variable to control job execution flow
├── deadlock # Example of problem and solution for deadlocks
├── livelock # Example of problem and solution for livelocks
├── race-condition # Example of problem and solution for race conditions
└── README.md
```
We can build the examples with the following commands.
```bash
cd /path/to/klips/cpp/multithreading/
mkdir build && cd build
cmake .. && cmake --build .
ls bin/
multithread-conditions multithread-deadlock multithread-livelock multithread-race-condition
```

View File

@@ -0,0 +1,26 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example of condition_variables in multithreaded C++ ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
cmake_minimum_required(VERSION 3.16)
# std::scoped_lock requires C++17
set(CMAKE_CXX_STANDARD 17)
add_compile_options("-Wall")
project(
#[[NAME]] ConditionVariables
VERSION 1.0
DESCRIPTION "Example of condition_variables in multithreaded C++"
LANGUAGES CXX
)
add_executable(
multithread-conditions driver.cpp
)
target_link_libraries(multithread-conditions pthread)

View File

@@ -0,0 +1,62 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example of condition_variables in multithreaded C++ ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include <chrono>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
static std::mutex mtx;
std::condition_variable cv;
bool processing = false;
// Starts a job that waits for kick-off from main
// + When job finishes, handoff result back to main via processing bool
void job(int32_t & shared) {
std::unique_lock uniqueLock(mtx);
cv.wait(uniqueLock, []()->bool {return processing;});
std::cout << std::this_thread::get_id()
<< " thread_A: Initial value of shared = " << shared << std::endl;
while (shared < INT32_MAX) {
shared++;
}
// We're no longer processing data
processing = false;
std::cout << std::this_thread::get_id()
<< " thread_A: Done working." << std::endl;
uniqueLock.unlock(); // Important! Unlock uniqueLock before we notify
// Notify main that we've finished, so it can proceed
cv.notify_one();
}
int main(const int argc, const char * argv[]) {
std::cout << "main() thread id: " << std::this_thread::get_id() << std::endl;
int32_t share = 0;
std::thread thread_A(job, std::ref(share));
mtx.lock();
std::this_thread::sleep_for(std::chrono::seconds(3));
share = INT32_MAX / 2;
processing = true;
mtx.unlock();
// Notify thread_A that its work can begin
cv.notify_one();
// Wait until thread_A finishes its work
std::unique_lock uniqueLock(mtx);
// Block execution until we are not processing
cv.wait(uniqueLock, []()->bool { return !processing;});
std::cout << std::this_thread::get_id() << " main(): final value of shared = "
<< share << std::endl;
thread_A.join();
return 0;
}

View File

@@ -0,0 +1,26 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example and solution for deadlocks in C++ ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
cmake_minimum_required(VERSION 3.16)
# std::scoped_lock requires C++17
set(CMAKE_CXX_STANDARD 17)
add_compile_options("-Wall")
project(
#[[NAME]] Deadlock
VERSION 1.0
DESCRIPTION "Example and solution for deadlocks in C++"
LANGUAGES CXX
)
add_executable(
multithread-deadlock driver.cpp
)
target_link_libraries(multithread-deadlock pthread)

View File

@@ -0,0 +1,189 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example and solution for deadlocks in C++ ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include <chrono>
#include <iostream>
#include <mutex>
#include <sstream>
#include <thread>
static std::mutex mtx_A, mtx_B, output;
// Helper function to output thread ID and string associated with mutex name
// + This must also be thread-safe, since we want threads to produce output
// + There is no bug or issue here; This is just in support of example output
void print_safe(const std::string & s) {
std::scoped_lock<std::mutex> scopedLock(output);
std::cout << s << std::endl;
}
// Helper function to convert std::thread::id to string
std::string id_string(const std::thread::id & id) {
std::stringstream stream;
stream << id;
return stream.str();
}
// In the two threads within this function, we have a problem
// + The mutex locks are acquired in reverse order, so they collide
// + This is called a deadlock; The program will *never* finish
void problem() {
std::thread thread_A([]()->void {
mtx_A.lock();
print_safe(id_string(std::this_thread::get_id()) + " thread_A: Locked A");
std::this_thread::sleep_for(std::chrono::seconds(1));
mtx_B.lock(); // We can't lock B! thread_B is using it
// The program will never reach this point in execution; We are in deadlock
print_safe(id_string(std::this_thread::get_id())
+ " thread_A: B has been unlocked, we can proceed!\n Locked B"
);
std::this_thread::sleep_for(std::chrono::seconds(1));
print_safe(id_string(std::this_thread::get_id())
+ " thread_A: Unlocking A, B..."
);
mtx_A.unlock();
mtx_B.unlock();
});
std::thread thread_B([]()->void {
mtx_B.lock();
print_safe(id_string(std::this_thread::get_id()) + " thread_B: Locked B");
std::this_thread::sleep_for(std::chrono::seconds(1));
mtx_A.lock(); // We can't lock A! thread_A is using it
// The program will never reach this point in execution; We are in deadlock
print_safe(id_string(std::this_thread::get_id())
+ " thread_B: A has been unlocked, we can proceed!\n Locked A"
);
std::this_thread::sleep_for(std::chrono::seconds(1));
print_safe(id_string(std::this_thread::get_id())
+ " thread_B: Unlocking B, A..."
);
mtx_B.unlock();
mtx_A.unlock();
});
// This offers a way out of the deadlock, so we can proceed to the solution
std::this_thread::sleep_for(std::chrono::seconds(2));
char input;
print_safe("\n"
+ id_string(std::this_thread::get_id())
+ " problem(): We are in a deadlock. \n"
+ " Enter y/Y to continue to the solution...\n"
);
while (std::cin >> input) {
if (input != 'Y' && input != 'y') continue;
else break;
}
print_safe(id_string(std::this_thread::get_id())
+ " problem(): Unlocking A, B..."
);
mtx_A.unlock();
mtx_B.unlock();
thread_A.join();
thread_B.join();
}
// std::lock will lock N mutex locks
// + If either is in use, execution will block until both are available to lock
void solution_A() {
std::thread thread_A([]()->void {
std::lock(mtx_A, mtx_B);
print_safe(id_string(std::this_thread::get_id()) + ": Locked A, B");
std::this_thread::sleep_for(std::chrono::seconds(1));
print_safe(id_string(std::this_thread::get_id()) + ": Unlocking A, B...");
mtx_A.unlock();
mtx_B.unlock();
});
std::thread thread_B([]()->void {
std::lock(mtx_B, mtx_A);
print_safe(id_string(std::this_thread::get_id()) + ": Locked B, A");
std::this_thread::sleep_for(std::chrono::seconds(1));
print_safe(id_string(std::this_thread::get_id()) + ": Unlocking B, A...");
mtx_B.unlock();
mtx_A.unlock();
});
thread_A.join();
thread_B.join();
}
// std::lock_guard is a C++11 object which can be constructed with 1 mutex
// + When the program leaves the scope of the guard, the mutex is unlocked
void solution_B() {
std::thread thread_A([]()->void {
// lock_guard will handle unlocking when program leaves this scope
std::lock_guard<std::mutex> guard_A(mtx_A), guard_B(mtx_B);
print_safe(id_string(std::this_thread::get_id()) + ": Locked A, B");
std::this_thread::sleep_for(std::chrono::seconds(1));
print_safe(id_string(std::this_thread::get_id()) + ": Unlocking A, B...");
// We don't need to explicitly unlock either mutex
});
std::thread thread_B([]()->void {
std::lock_guard<std::mutex> guard_B(mtx_B), guard_A(mtx_A);
print_safe(id_string(std::this_thread::get_id()) + ": Locked B, A");
std::this_thread::sleep_for(std::chrono::seconds(1));
print_safe(id_string(std::this_thread::get_id()) + ": Unlocking B, A...");
// We don't need to explicitly unlock either mutex
});
thread_A.join();
thread_B.join();
}
// std::scoped_lock is a C++17 object that can be constructed with N mutex
// + When the program leaves this scope, all N mutex will be unlocked
void solution_C() {
std::thread thread_A([]()->void {
// scoped_lock will handle unlocking when program leaves this scope
std::scoped_lock scopedLock(mtx_A, mtx_B);
print_safe(id_string(std::this_thread::get_id()) + ": Locked A, B");
std::this_thread::sleep_for(std::chrono::seconds(1));
print_safe(id_string(std::this_thread::get_id()) + ": Unlocking A, B...");
// We don't need to explicitly unlock either mutex
});
std::thread thread_B([]()->void {
std::scoped_lock scopedLock(mtx_B, mtx_A);
print_safe(id_string(std::this_thread::get_id()) + ": Locked B, A");
std::this_thread::sleep_for(std::chrono::seconds(1));
print_safe(id_string(std::this_thread::get_id()) + ": Unlocking B, A...");
// We don't need to explicitly unlock either mutex
});
thread_A.join();
thread_B.join();
}
int main(const int argc, const char * argv[]) {
std::cout << "main() thread id: " << std::this_thread::get_id() << std::endl;
problem();
print_safe("\nsolution_A, using std::lock\n");
solution_A();
print_safe("\nsolution_B, using std::lock_guard\n");
solution_B();
print_safe("\nsolution_C, using std::scoped_lock\n");
solution_C();
return 0;
}

View File

@@ -0,0 +1,26 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example and solution for livelocks in C++ ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
cmake_minimum_required(VERSION 3.16)
# std::scoped_lock requires C++17
set(CMAKE_CXX_STANDARD 17)
add_compile_options("-Wall")
project(
#[[NAME]] LiveLock
VERSION 1.0
DESCRIPTION "Example and solution for livelocks in C++"
LANGUAGES CXX
)
add_executable(
multithread-livelock driver.cpp
)
target_link_libraries(multithread-livelock pthread)

View File

@@ -0,0 +1,117 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example and solution for livelocks in C++ ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include <chrono>
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
static std::mutex mtx_A, mtx_B, output;
// Helper function to output thread ID and string associated with mutex name
// + This must also be thread-safe, since we want threads to produce output
// + There is no bug or issue here; This is just in support of example output
void print_safe(const std::string & s) {
std::scoped_lock<std::mutex> scopedLock(output);
std::cout << s << std::endl;
}
void problem() {
// Construct a vector with 5 agreed-upon times to synchronize loops in threads
typedef std::chrono::time_point<std::chrono::steady_clock,
std::chrono::steady_clock::duration> time_point;
std::vector<time_point> waitTime(6);
for (uint8_t i = 0; i < 6; i++) {
waitTime[i] = std::chrono::steady_clock::now()+std::chrono::seconds(1+i);
}
std::thread thread_A([waitTime]()->void {
uint8_t count = 0; // Used to select time slot from waitTime vector
bool done = false;
while (!done) {
count++;
std::lock_guard l(mtx_A);
std::cout << std::this_thread::get_id() << " thread_A: Lock A\n";
// Wait until the next time slot to continue
// + Helps to show example of livelock by ensuring B is not available
std::this_thread::sleep_until(waitTime[count]);
std::cout << std::this_thread::get_id() << " thread_A: Requesting B\n";
if (mtx_B.try_lock()) {
done = true;
std::cout << std::this_thread::get_id()
<< " thread_A: Acquired locks for A, B! Done.\n";
}
else {
std::cout << std::this_thread::get_id()
<< " thread_A: Can't lock B, unlocking A\n";
}
}
mtx_B.unlock();
});
std::thread thread_B([waitTime]()->void {
// As an example, enter livelock for only 5 iterations
// + Also used to select time slot from waitTime vector
uint8_t count = 0;
bool done = false;
while (!done && count < 5) {
count++;
std::lock_guard l(mtx_B);
// Wait until the next time slot to continue
// + Helps to show example of livelock by ensuring A is not available
std::this_thread::sleep_until(waitTime[count]);
if (mtx_A.try_lock()) {
// The program will never reach this point in the code
// + The only reason livelock ends is because count > 5
done = true;
}
}
});
thread_A.join();
thread_B.join();
}
// The solution below uses std::scoped_lock to avoid the livelock problem
void solution() {
std::thread thread_A([]()->void {
for (int i = 0; i < 5; i++) {
// Increase wait time with i
// + To encourage alternating lock ownership between threads
std::this_thread::sleep_for(std::chrono::milliseconds(100 * i));
std::scoped_lock l(mtx_A, mtx_B);
std::cout << std::this_thread::get_id()
<< " thread_A: Acquired locks for A, B!" << std::endl;
}
});
std::thread thread_B([]()->void {
for (int i = 0; i < 5; i++) {
std::this_thread::sleep_for(std::chrono::milliseconds(100 * i));
std::scoped_lock l(mtx_B, mtx_A);
std::cout << std::this_thread::get_id()
<< " thread_B: Acquired locks for B, A!" << std::endl;
}
});
thread_A.join();
thread_B.join();
}
int main(const int argc, const char * argv[]) {
std::cout << "main() thread id: " << std::this_thread::get_id() << std::endl;
problem();
std::cout << "\nSolution:\n\n";
solution();
return 0;
}

View File

@@ -0,0 +1,22 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example and solution for race conditions in C++ ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
cmake_minimum_required(VERSION 3.16)
project(
#[[NAME]] RaceCondition
VERSION 1.0
DESCRIPTION "Example and solution for race conditions"
LANGUAGES CXX
)
add_executable(
multithread-race-condition driver.cpp
)
target_link_libraries(multithread-race-condition pthread)

View File

@@ -0,0 +1,64 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example of a race condition problem and solution ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
void problem() {
std::vector<std::thread> threads;
const uint8_t thread_count = 5;
// With no mutex lock, the final value will vary in the range 1000000-5000000
// + Threads will modify x simultaneously, so some iterations will be lost
// + x will have same initial value entering this loop on different threads
uint32_t x = 0;
for (uint8_t i = 0; i < thread_count; i++) {
threads.emplace_back([&x](){
for (uint32_t i = 0; i < 1000000; i++) {
x = x + 1;
};
});
}
// Ensure the function doesn't continue until all threads are finished
// + There's no issue here, the issue is in how `x` is accessed above
for (auto &thread : threads) thread.join();
std::cout << x << std::endl;
}
// Create mutex lock to prevent threads from modifying same value simultaneously
static std::mutex mtx;
void solution() {
std::vector<std::thread> threads;
const uint8_t thread_count = 5;
uint32_t x = 0;
for (uint8_t i = 0; i < thread_count; i++) {
threads.emplace_back([&x](){
// The first thread that arrives here will 'lock' other threads from passing
// + Once first thread finishes, the next thread will resume
// + This process repeats until all threads finish
std::lock_guard<std::mutex> lock(mtx);
for (uint32_t i = 0; i < 1000000; i++) {
x = x + 1;
};
});
}
// Ensure the function doesn't continue until all threads are finished
for (auto &thread : threads) thread.join();
std::cout << x << std::endl;
}
int main(const int argc, const char * argv[]) {
// Result will vary from 1000000-5000000
problem();
// Result will always be 5000000
solution();
return 0;
}

24
cpp/qt/CMakeLists.txt Normal file
View File

@@ -0,0 +1,24 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: A root project for practicing Qt 6 projects in C++ ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
cmake_minimum_required(VERSION 3.15)
project(
#[[NAME]] Klips
VERSION 1.0
DESCRIPTION "A root project for several small Qt6 practice projects"
LANGUAGES CXX
)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
add_compile_options("-Wall")
add_subdirectory(designer)
add_subdirectory(designer-plugin)
add_subdirectory(designer-plugin-collection)
add_subdirectory(slots)

32
cpp/qt/README.md Normal file
View File

@@ -0,0 +1,32 @@
# Cpp
```bash
shaunrd0/klips/cpp/qt/
├── designer # Using Qt Designer to create application GUI
├── designer-plugin # Adding custom widgets as Qt Designer plugins
├── designer-plugin-collection # Adding a collection of widget plugins to Qt Designer
└── README.md
```
This directory contains a `CMakeLists.txt`, which can be selected to open as a
project within your preferred IDE. From there, all nested examples can be built,
debugged, and ran.
The plugin examples will need to be installed for Qt Designer integration to work.
On Linux, Qt Designer looks under `/some/path/to/Qt/Tools/QtCreator/lib/Qt/plugins/designer/`.
On windows or Mac, this path may differ. Unfortunately I don't have these machines to test for myself.
```bash
cd klips/cpp/qt/designer-plugin-collection
mkdir build && cd build
cmake .. && cmake --build . --target install
```
After installing the plugin collection example above, we can open Qt Creator and navigate to the Designer.
We should see the custom collection is available within the Designer, and the contents of the widgets render correctly in the application view.
![side-panel-view.png](side-panel-view.png)
![plugin-render-view.png](plugin-render-view.png)

View File

@@ -0,0 +1,110 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Example of making a collection of widget plugins for Qt Designer ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
cmake_minimum_required(VERSION 3.15)
project(
#[[NAME]] DesignerPluginCollection
VERSION 1.0
DESCRIPTION "Example of a widget plugin collection for Qt Designer"
LANGUAGES CXX
)
include(GenerateExportHeader)
add_compile_options(-Wall)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_SHARED_MODULE_PREFIX "")
set(QT_DIR "$ENV{HOME}/Code/Clones/Qt/" CACHE PATH "Path to Qt6")
# Qt Designer will look in different locations if WIN / Unix.
# These paths are for using Qt Designer integrated within Qt Creator.
# Standalone Qt Designer may use different paths.
if (WIN32)
set(QT_PLUGIN_INSTALL_DIR
"${QT_DIR}/Tools/QtCreator/bin/plugins/designer"
)
# This path may be different on windows. I have not tested this.
set(QT_PLUGIN_LIBRARY_DIR
"${QT_DIR}/Tools/QtCreator/lib/Qt/lib"
)
else()
set(QT_PLUGIN_INSTALL_DIR
"${QT_DIR}/Tools/QtCreator/lib/Qt/plugins/designer"
)
set(QT_PLUGIN_LIBRARY_DIR
"${QT_DIR}/Tools/QtCreator/lib/Qt/lib"
)
endif()
# This should be set to your Qt6 installation directory.
set(QT_INSTALL_DIR "${QT_DIR}/6.3.1/gcc_64/" CACHE PATH "Path to Qt6 install")
list(APPEND CMAKE_PREFIX_PATH "${QT_INSTALL_DIR}")
find_package(Qt6 REQUIRED COMPONENTS UiPlugin Core Gui Widgets)
# Creating a library with two plugins for the collection.
qt_add_library(widget-plugin-library
textview.cpp textview.h
widgetplugin.cpp widgetplugin.h
)
target_sources(widget-plugin-library PRIVATE
textview.cpp textview.h
treeview.cpp treeview.h
widgetplugin.cpp widgetplugin.h
)
set_target_properties(widget-plugin-library PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(widget-plugin-library
PUBLIC Qt::UiPlugin Qt::Core Qt::Gui Qt::Widgets
)
install(TARGETS widget-plugin-library
RUNTIME DESTINATION "${QT_PLUGIN_LIBRARY_DIR}"
BUNDLE DESTINATION "${QT_PLUGIN_LIBRARY_DIR}"
LIBRARY DESTINATION "${QT_PLUGIN_LIBRARY_DIR}"
)
generate_export_header(widget-plugin-library)
# Creating the collection
qt_add_library(widget-plugin-collection
widgetplugincollection.cpp widgetplugincollection.h
)
target_link_libraries(widget-plugin-collection
Qt6::Widgets Qt6::UiPlugin widget-plugin-library
)
install(TARGETS widget-plugin-collection
RUNTIME DESTINATION "${QT_PLUGIN_INSTALL_DIR}"
BUNDLE DESTINATION "${QT_PLUGIN_INSTALL_DIR}"
LIBRARY DESTINATION "${QT_PLUGIN_INSTALL_DIR}"
)
# Application that will use the widget plugin
set(APP_DIR ${CMAKE_CURRENT_SOURCE_DIR})
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/app-dir.h.in"
"${CMAKE_CURRENT_SOURCE_DIR}/app-dir.h"
@ONLY
)
qt_add_executable(widget-app
widgetapp.cpp widgetapp.h widgetapp.ui
main.cpp
)
target_link_libraries(widget-app
PRIVATE Qt::Widgets widget-plugin-library
)

View File

@@ -0,0 +1,6 @@
#ifndef APPDIR_H_IN
#define APPDIR_H_IN
#define APP_DIR "/home/kapper/Code/klips/cpp/qt/designer-plugin-collection"
#endif // APPDIR_H_IN

View File

@@ -0,0 +1,6 @@
#ifndef APPDIR_H_IN
#define APPDIR_H_IN
#define APP_DIR "@APP_DIR@"
#endif // APPDIR_H_IN

View File

@@ -0,0 +1,18 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Main driver fprogram for practice using signals and slots in Qt ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "widgetapp.h"
#include <QApplication>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
WidgetApp widgetApp;
widgetApp.show();
return app.exec();
}

View File

@@ -0,0 +1,10 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Text viewer for signals and slots examples ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "textview.h"

View File

@@ -0,0 +1,44 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Text viewer for signals and slots examples ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#ifndef KLIPS_TEXTVIEW_H
#define KLIPS_TEXTVIEW_H
#include "widget-plugin-library_export.h"
#include <QPlainTextEdit>
class WIDGET_PLUGIN_LIBRARY_EXPORT TextView : public QPlainTextEdit {
Q_OBJECT
public:
explicit TextView(QWidget *parent = nullptr) : QPlainTextEdit(parent) {
appendPlainText("This is an example of a custom QTextView widget.");
}
~TextView() = default;
QString includeFile() const { return QStringLiteral("text-view.h"); };
public:
signals:
void sendTest();
private:
signals:
void sentTestPrivate();
public slots:
void test() { appendPlainText("Test signal received by TextView."); }
void testArgs(const QString &message) { appendPlainText(message); }
private slots:
void testPrivate() {}
};
#endif // KLIPS_TEXTVIEW_H

View File

@@ -0,0 +1,10 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Tree viewer ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "treeview.h"

View File

@@ -0,0 +1,36 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Tree viewer ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#ifndef KLIPS_TREEVIEW_H
#define KLIPS_TREEVIEW_H
#include "widget-plugin-library_export.h"
#include <app-dir.h>
#include <QFileSystemModel>
#include <QSortFilterProxyModel>
#include <QTreeView>
class WIDGET_PLUGIN_LIBRARY_EXPORT TreeView : public QTreeView {
Q_OBJECT
public:
explicit TreeView(QWidget *parent = nullptr) : QTreeView(parent) {
QFileSystemModel *model = new QFileSystemModel;
QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this);
QModelIndex rootModelIndex = model->setRootPath(APP_DIR);
proxy->setSourceModel(model);
setModel(proxy);
setRootIndex(proxy->mapFromSource(rootModelIndex));
}
~TreeView() = default;
};
#endif // KLIPS_TREEVIEW_H

View File

@@ -0,0 +1,15 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Application that uses widget from the collection ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "widgetapp.h"
WidgetApp::WidgetApp(QWidget *parent) : QMainWindow(parent) {
m_widgetApp = new Ui::MainWindow;
m_widgetApp->setupUi(this);
}

View File

@@ -0,0 +1,37 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Application that uses a custom Qt Designer widget plugin ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#ifndef KLIPS_WIDGETAPP_H
#define KLIPS_WIDGETAPP_H
#include <QDockWidget>
#include <QMainWindow>
#include <QPlainTextEdit>
#include <QVBoxLayout>
#include <QWidget>
#include "ui_widgetapp.h"
class WidgetApp : public QMainWindow {
Q_OBJECT
public:
explicit WidgetApp(QWidget *parent = nullptr);
~WidgetApp() = default;
Ui::MainWindow * m_widgetApp;
public:
signals:
void sendTest();
public slots:
void test(){};
};
#endif // KLIPS_WIDGETAPP_H

View File

@@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="TextView" name="text-view_4" native="true"/>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionOption1"/>
<addaction name="actionOption2"/>
<addaction name="separator"/>
<addaction name="actionCategory_2"/>
</widget>
<widget class="QMenu" name="menuEdit">
<property name="title">
<string>Edit</string>
</property>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>Help</string>
</property>
</widget>
<addaction name="menuFile"/>
<addaction name="menuEdit"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QDockWidget" name="dockWidget_5">
<attribute name="dockWidgetArea">
<number>1</number>
</attribute>
<widget class="QWidget" name="dockWidgetContents_8">
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="TreeView" name="tree-view"/>
</item>
</layout>
</widget>
</widget>
<action name="actionOption1">
<property name="text">
<string>Option1</string>
</property>
</action>
<action name="actionOption2">
<property name="text">
<string>Option2</string>
</property>
</action>
<action name="actionCategory_2">
<property name="text">
<string>Section 2</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>TreeView</class>
<extends>QWidget</extends>
<header>treeview.h</header>
</customwidget>
<customwidget>
<class>TextView</class>
<extends>QWidget</extends>
<header>textview.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,52 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Example of a generic Qt Designer widget plugin ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "widgetplugin.h"
#include "textview.h"
#include <QtPlugin>
#include <utility>
WidgetPlugin::WidgetPlugin(QString group, QString name,
WidgetPlugin::Factory factory)
: m_group(std::move(group)), m_name(std::move(name)),
m_includeFile(name + ".h"), m_factory(std::move(factory)) {}
WidgetPlugin::WidgetPlugin(QString group, QString name, QString include,
WidgetPlugin::Factory factory)
: m_group(std::move(group)), m_name(std::move(name)),
m_includeFile(std::move(include)), m_factory(std::move(factory)) {}
QString WidgetPlugin::toolTip() const { return {}; }
QString WidgetPlugin::whatsThis() const { return {}; }
QIcon WidgetPlugin::icon() const { return {}; }
bool WidgetPlugin::isContainer() const { return false; }
QString WidgetPlugin::group() const { return m_group; }
QString WidgetPlugin::name() const { return m_name; }
// TODO: The generated UI headers do not use this member appropriately.
QString WidgetPlugin::includeFile() const { return m_includeFile; }
QWidget *WidgetPlugin::createWidget(QWidget *parent) {
return m_factory(parent);
}
bool WidgetPlugin::isInitialized() const { return m_initialized; }
void WidgetPlugin::initialize(QDesignerFormEditorInterface *) {
if (m_initialized)
return;
m_initialized = true;
}

View File

@@ -0,0 +1,52 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Example Qt Designer widget plugin ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#ifndef KLIPS_WIDGETPLUGIN_H
#define KLIPS_WIDGETPLUGIN_H
#include <QDesignerCustomWidgetInterface>
class WidgetPlugin : public QObject, public QDesignerCustomWidgetInterface {
Q_OBJECT
Q_PLUGIN_METADATA(IID "com.Klips.WidgetPlugin")
Q_INTERFACES(QDesignerCustomWidgetInterface)
using Factory = std::function<QWidget *(QWidget *)>;
public:
WidgetPlugin(QString group, QString name, Factory factory);
WidgetPlugin(QString group, QString name, QString include, Factory factory);
explicit WidgetPlugin(QObject *parent = nullptr) : QObject(parent) {}
~WidgetPlugin() = default;
public:
[[nodiscard]] QString group() const override;
[[nodiscard]] QString name() const override;
[[nodiscard]] QString includeFile() const override;
QWidget *createWidget(QWidget *parent) override;
[[nodiscard]] QString toolTip() const override;
[[nodiscard]] QString whatsThis() const override;
[[nodiscard]] QIcon icon() const override;
[[nodiscard]] bool isContainer() const override;
[[nodiscard]] bool isInitialized() const override;
void initialize(QDesignerFormEditorInterface *core) override;
private:
bool m_initialized = false;
QString m_group;
QString m_name;
QString m_includeFile;
Factory m_factory;
};
#endif // KLIPS_WIDGETPLUGIN_H

View File

@@ -0,0 +1,28 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Collection of widget plugins for Qt Designer ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "widgetplugincollection.h"
#include "textview.h"
#include "treeview.h"
#include "widgetplugin.h"
WidgetPluginCollection::WidgetPluginCollection(QObject *parent)
: QObject(parent), m_collectionName("Klips Widget Plugin Collection") {
m_collection = {
new WidgetPlugin(m_collectionName, "Text View Widget", "text-view.h",
[](QWidget *parent) { return new TextView(parent); }),
new WidgetPlugin(m_collectionName, "tree-view",
[](QWidget *parent) { return new TreeView(parent); }),
};
}
QList<QDesignerCustomWidgetInterface *>
WidgetPluginCollection::customWidgets() const {
return m_collection;
}

View File

@@ -0,0 +1,22 @@
#ifndef DESIGNERPLUGINCOLLECTION_WIDGETPLUGINCOLLECTION_H
#define DESIGNERPLUGINCOLLECTION_WIDGETPLUGINCOLLECTION_H
#include <QDesignerCustomWidgetCollectionInterface>
class WidgetPluginCollection : public QObject,
public QDesignerCustomWidgetCollectionInterface {
Q_OBJECT
Q_PLUGIN_METADATA(IID "com.Klips.WidgetPluginCollection")
Q_INTERFACES(QDesignerCustomWidgetCollectionInterface)
public:
explicit WidgetPluginCollection(QObject *parent = nullptr);
[[nodiscard]] QList<QDesignerCustomWidgetInterface *> customWidgets() const;
private:
QList<QDesignerCustomWidgetInterface *> m_collection;
QString m_collectionName;
};
#endif // DESIGNERPLUGINCOLLECTION_WIDGETPLUGINCOLLECTION_H

View File

@@ -0,0 +1,76 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Example of making widget plugins for Qt Designer ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
cmake_minimum_required(VERSION 3.15)
project(
#[[NAME]] DesignerPlugin
VERSION 1.0
DESCRIPTION "Example of a widget plugin for Qt Designer"
LANGUAGES CXX
)
include(GenerateExportHeader)
add_compile_options(-Wall)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_SHARED_MODULE_PREFIX "")
set(QT_DIR "$ENV{HOME}/Code/Clones/Qt/" CACHE PATH "Path to Qt6")
# Qt Designer will look in different locations if WIN / Unix.
if (WIN32)
set(QT_PLUGIN_INSTALL_DIR
"${QT_DIR}/Tools/QtCreator/bin/plugins/designer"
)
else()
set(QT_PLUGIN_INSTALL_DIR
"${QT_DIR}/Tools/QtCreator/lib/Qt/plugins/designer"
)
endif()
# This should be set to your Qt6 installation directory.
set(QT_INSTALL_DIR "${QT_DIR}/6.3.1/gcc_64/" CACHE PATH "Path to Qt6 install")
list(APPEND CMAKE_PREFIX_PATH "${QT_INSTALL_DIR}")
find_package(Qt6 REQUIRED COMPONENTS UiPlugin Core Gui Widgets)
# Creating the plugin
qt_add_library(widget-plugin)
target_sources(widget-plugin PRIVATE
text-view.cpp text-view.h
widget-plugin.cpp widget-plugin.h
)
set_target_properties(widget-plugin PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(widget-plugin PUBLIC
Qt::UiPlugin Qt::Core Qt::Gui Qt::Widgets
)
install(TARGETS widget-plugin
RUNTIME DESTINATION "${QT_PLUGIN_INSTALL_DIR}"
BUNDLE DESTINATION "${QT_PLUGIN_INSTALL_DIR}"
LIBRARY DESTINATION "${QT_PLUGIN_INSTALL_DIR}"
)
# Application that will use the widget plugin
qt_add_executable(widget-app
widget-app.cpp widget-app.h widget-app.ui
main.cpp
)
target_link_libraries(widget-app PRIVATE
Qt::Widgets widget-plugin
)

View File

@@ -0,0 +1,18 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Main driver fprogram for practice using signals and slots in Qt ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "widget-app.h"
#include <QApplication>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
WidgetApp widgetApp;
widgetApp.show();
return app.exec();
}

View File

@@ -0,0 +1,10 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Text viewer for signals and slots examples ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "text-view.h"

View File

@@ -0,0 +1,40 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Text viewer for signals and slots examples ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#ifndef KLIPS_TEXTVIEW_H
#define KLIPS_TEXTVIEW_H
#include <QPlainTextEdit>
class TextView : public QPlainTextEdit {
Q_OBJECT
public:
explicit TextView(QWidget *parent = nullptr) : QPlainTextEdit(parent) { }
~TextView() = default;
public:
signals:
void sendTest();
private:
signals:
void sentTestPrivate();
public slots:
void test() { appendPlainText("Test signal received by TextView."); }
void testArgs(const QString &message) { appendPlainText(message); }
private slots:
void testPrivate() {}
};
#endif // KLIPS_TEXTVIEW_H

View File

@@ -0,0 +1,16 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Application that uses a custom Qt Designer widget plugin ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "widget-app.h"
#include "widget-plugin.h"
WidgetApp::WidgetApp(QWidget *parent) : QMainWindow(parent) {
m_ui = new Ui::MainWindow;
m_ui->setupUi(this);
}

View File

@@ -0,0 +1,38 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Application that uses a custom Qt Designer widget plugin ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#ifndef KLIPS_WIDGETAPP_H
#define KLIPS_WIDGETAPP_H
#include <QDockWidget>
#include <QMainWindow>
#include <QPlainTextEdit>
#include <QVBoxLayout>
#include <QWidget>
#include "ui_widget-app.h"
class WidgetApp : public QMainWindow {
Q_OBJECT
public:
WidgetApp(QWidget *parent = nullptr);
~WidgetApp() = default;
Ui::MainWindow *m_ui;
public:
signals:
void sendTest();
public slots:
void test(){};
};
#endif // KLIPS_WIDGETAPP_H

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout"/>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,67 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Example Qt Designer widget plugin ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "widget-plugin.h"
#include "text-view.h"
#include <QtPlugin>
QString WidgetPlugin::toolTip() const
{
return {};
}
QString WidgetPlugin::whatsThis() const
{
return {};
}
QIcon WidgetPlugin::icon() const
{
return {};
}
bool WidgetPlugin::isContainer() const
{
return false;
}
QString WidgetPlugin::group() const
{
return m_group;
}
QString WidgetPlugin::name() const
{
return QStringLiteral("KlipsWidgetPlugin");
}
QString WidgetPlugin::includeFile() const
{
return QStringLiteral("widget-plugin.h");
}
QWidget *WidgetPlugin::createWidget(QWidget *parent)
{
return new TextView(parent);
}
bool WidgetPlugin::isInitialized() const
{
return m_initialized;
}
void WidgetPlugin::initialize(QDesignerFormEditorInterface *)
{
if (m_initialized)
return;
m_initialized = true;
}

View File

@@ -0,0 +1,45 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Example Qt Designer widget plugin ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#ifndef KLIPS_WIDGETPLUGIN_H
#define KLIPS_WIDGETPLUGIN_H
#include <QtUiPlugin/QDesignerCustomWidgetInterface>
class WidgetPlugin : public QObject, public QDesignerCustomWidgetInterface {
Q_OBJECT
Q_PLUGIN_METADATA(IID "com.Klips.WidgetPlugin")
Q_INTERFACES(QDesignerCustomWidgetInterface)
public:
explicit WidgetPlugin(QObject *parent = nullptr) : QObject(parent) {}
~WidgetPlugin() = default;
public:
QString group() const override;
QString name() const override;
QString includeFile() const override;
QWidget *createWidget(QWidget *parent) override;
QString toolTip() const override;
QString whatsThis() const override;
QIcon icon() const override;
bool isContainer() const override;
bool isInitialized() const override;
void initialize(QDesignerFormEditorInterface *core) override;
private:
bool m_initialized = false;
QString m_group;
QString m_name;
QString m_includeFile;
};
#endif // KLIPS_WIDGETPLUGIN_H

View File

@@ -0,0 +1,52 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Practice project for using Qt Designer with custom C++ widgets ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
cmake_minimum_required(VERSION 3.15)
project(
#[[NAME]] Designer
VERSION 0.1
DESCRIPTION "Practice using Qt designer for desktop applications"
LANGUAGES CXX
)
add_compile_options(-Wall)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(QT_DIR "$ENV{HOME}/Code/Clones/Qt/6.3.1/gcc_64/" CACHE PATH "Path to Qt6")
list(APPEND CMAKE_PREFIX_PATH "${QT_DIR}")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
qt_add_executable(designer
designer.cpp designer.h designer.ui
debugconsole.h debugconsole.cpp debugconsole.ui
texteditor.h texteditor.cpp texteditor.ui
treeview.h treeview.cpp treeview.ui
main.cpp
)
set_target_properties(designer PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(designer PUBLIC Qt::Core Qt::Gui Qt::Widgets)
install(TARGETS designer
RUNTIME DESTINATION "install/designer"
BUNDLE DESTINATION "install/designer"
LIBRARY DESTINATION "install/designer"
)

View File

@@ -0,0 +1,18 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Debug console widget made in Qt Designer with C++ ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "debugconsole.h"
#include "ui_debugconsole.h"
DebugConsole::DebugConsole(QWidget *parent)
: QDockWidget(parent), ui(new Ui::DebugConsole) {
ui->setupUi(this);
}
DebugConsole::~DebugConsole() { delete ui; }

View File

@@ -0,0 +1,29 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Debug console widget made in Qt Designer with C++ ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#ifndef DEBUGCONSOLE_H
#define DEBUGCONSOLE_H
#include "ui_debugconsole.h"
#include <QDockWidget>
class DebugConsole : public QDockWidget {
Q_OBJECT
public:
explicit DebugConsole(QWidget *parent = nullptr);
~DebugConsole();
inline QPlainTextEdit *getConsole() { return ui->plainTextEdit; }
private:
Ui::DebugConsole *ui;
};
#endif // DEBUGCONSOLE_H

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DebugConsole</class>
<widget class="QDockWidget" name="DebugConsole">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>DockWidget</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPlainTextEdit" name="plainTextEdit"/>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,30 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: MainWindow for Qt Designer desktop application ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include <QtWidgets>
#include "debugconsole.h"
#include "designer.h"
#include "texteditor.h"
#include "treeview.h"
Designer::Designer(QWidget *parent)
: QMainWindow(parent), designer_(new Ui::Designer) {
designer_->setupUi(this);
setCentralWidget(new TextEditor);
auto debugConsole = new DebugConsole;
debugConsole->getConsole()->appendPlainText("Test 1");
debugConsole->getConsole()->appendPlainText("Test 2");
auto treeView = new TreeView;
addDockWidget(Qt::DockWidgetArea::BottomDockWidgetArea, debugConsole);
addDockWidget(Qt::DockWidgetArea::BottomDockWidgetArea, treeView);
sendTest();
}

View File

@@ -0,0 +1,39 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: MainWindow for Qt Designer desktop application ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#ifndef KLIPS_SLOTSAPP_H
#define KLIPS_SLOTSAPP_H
#include <QDockWidget>
#include <QMainWindow>
#include <QPlainTextEdit>
#include <QVBoxLayout>
#include <QWidget>
#include "ui_designer.h"
class Designer : public QMainWindow {
Q_OBJECT
public:
Designer(QWidget *parent = nullptr);
~Designer() = default;
public:
signals:
void sendTest();
public slots:
void test(){};
private:
Ui::Designer *designer_;
};
#endif // KLIPS_SLOTSAPP_H

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Designer</class>
<widget class="QMainWindow" name="Designer">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget"/>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
</widget>
<addaction name="menuFile"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

19
cpp/qt/designer/main.cpp Normal file
View File

@@ -0,0 +1,19 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Main driver program for Qt Designer desktop application.. ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "designer.h"
#include <QApplication>
int main(int argc, char * argv[]) {
QApplication app(argc, argv);
Designer qtk;
qtk.show();
// Show widget.
return app.exec();
}

View File

@@ -0,0 +1,23 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Dockable text editor widget made in Qt Designer with C++ ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "texteditor.h"
#include "ui_texteditor.h"
TextEditor::TextEditor(QWidget *parent) :
QDockWidget(parent),
ui(new Ui::TextEditor)
{
ui->setupUi(this);
}
TextEditor::~TextEditor()
{
delete ui;
}

View File

@@ -0,0 +1,31 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Dockable text editor widget made in Qt Designer with C++ ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#ifndef TEXTEDITOR_H
#define TEXTEDITOR_H
#include <QDockWidget>
namespace Ui {
class TextEditor;
}
class TextEditor : public QDockWidget
{
Q_OBJECT
public:
explicit TextEditor(QWidget *parent = nullptr);
~TextEditor();
private:
Ui::TextEditor *ui;
};
#endif // TEXTEDITOR_H

View File

@@ -0,0 +1,17 @@
<ui version="4.0">
<class>TextEditor</class>
<widget class="QDockWidget" name="TextEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>DockWidget</string>
</property>
<widget class="QWidget" name="dockWidgetContents"/>
</widget>
</ui>

View File

@@ -0,0 +1,23 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Dockable tree view widget made in Qt Designer with C++ ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "treeview.h"
#include "ui_treeview.h"
TreeView::TreeView(QWidget *parent) :
QDockWidget(parent),
ui(new Ui::TreeView)
{
ui->setupUi(this);
}
TreeView::~TreeView()
{
delete ui;
}

View File

@@ -0,0 +1,31 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Dockable tree view widget made in Qt Designer with C++ ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#ifndef TREEVIEW_H
#define TREEVIEW_H
#include <QDockWidget>
namespace Ui {
class TreeView;
}
class TreeView : public QDockWidget
{
Q_OBJECT
public:
explicit TreeView(QWidget *parent = nullptr);
~TreeView();
private:
Ui::TreeView *ui;
};
#endif // TREEVIEW_H

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TreeView</class>
<widget class="QDockWidget" name="TreeView">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>DockWidget</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTreeView" name="treeView"/>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

BIN
cpp/qt/side-panel-view.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -0,0 +1,50 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Practice project for using signals and slots in Qt ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
cmake_minimum_required(VERSION 3.15)
project(
#[[NAME]] Slots
VERSION 1.0
DESCRIPTION "Practice using signals and slots in Qt 6"
LANGUAGES CXX
)
add_compile_options(-Wall)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(QT_DIR "$ENV{HOME}/Code/Clones/Qt/6.3.1/gcc_64/" CACHE PATH "Path to Qt6")
list(APPEND CMAKE_PREFIX_PATH "${QT_DIR}")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
qt_add_executable(slots
text-view.cpp text-view.h
slots-app.cpp slots-app.h
main.cpp
)
set_target_properties(slots PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(slots PUBLIC Qt::Core Qt::Gui Qt::Widgets)
install(TARGETS slots
RUNTIME DESTINATION "install/slots"
BUNDLE DESTINATION "install/slots"
LIBRARY DESTINATION "install/slots"
)

18
cpp/qt/slots/main.cpp Normal file
View File

@@ -0,0 +1,18 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Main driver fprogram for practice using signals and slots in Qt ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "slots-app.h"
#include <QApplication>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
SlotsApp slotsApp;
slotsApp.show();
return app.exec();
}

View File

@@ -0,0 +1,42 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: MainWindow application for practice using signals and slots in Qt ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "slots-app.h"
#include "text-view.h"
SlotsApp::SlotsApp(QWidget *parent) : QMainWindow(parent) {
auto textBox = new QPlainTextEdit;
auto textView = new TextView;
auto frame = new QFrame;
auto dock = new QDockWidget(this);
auto dockWidget = new QWidget;
auto dockWidgetLayout = new QVBoxLayout;
dockWidgetLayout->addWidget(frame);
dockWidgetLayout->addWidget(textBox);
dockWidget->setLayout(dockWidgetLayout);
dock->setWidget(dockWidget);
auto dock2 = new QDockWidget(this);
auto dockWidget2 = new QWidget;
auto dockWidgetLayout2 = new QVBoxLayout;
dockWidgetLayout2->addWidget(textView);
dockWidget2->setLayout(dockWidgetLayout2);
dock2->setWidget(dockWidget2);
textBox->setReadOnly(true);
textBox->appendPlainText("Test 1");
textBox->appendPlainText("Test 2");
addDockWidget(Qt::DockWidgetArea::BottomDockWidgetArea, dock);
addDockWidget(Qt::DockWidgetArea::BottomDockWidgetArea, dock2);
connect(this, &SlotsApp::sendTest, textView, &TextView::test);
sendTest();
}

34
cpp/qt/slots/slots-app.h Normal file
View File

@@ -0,0 +1,34 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: MainWindow application for practice using signals and slots in Qt ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#ifndef KLIPS_SLOTSAPP_H
#define KLIPS_SLOTSAPP_H
#include <QDockWidget>
#include <QMainWindow>
#include <QPlainTextEdit>
#include <QVBoxLayout>
#include <QWidget>
class SlotsApp : public QMainWindow {
Q_OBJECT
public:
SlotsApp(QWidget *parent = nullptr);
~SlotsApp() = default;
public:
signals:
void sendTest();
public slots:
void test(){};
};
#endif // KLIPS_SLOTSAPP_H

View File

@@ -0,0 +1,10 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Text viewer for signals and slots examples ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "text-view.h"

40
cpp/qt/slots/text-view.h Normal file
View File

@@ -0,0 +1,40 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Text viewer for signals and slots examples ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#ifndef KLIPS_TEXTVIEW_H
#define KLIPS_TEXTVIEW_H
#include <QPlainTextEdit>
class TextView : public QPlainTextEdit {
Q_OBJECT
public:
TextView(QWidget *parent = nullptr) {}
~TextView() = default;
public:
signals:
void sendTest()QWidget;
private:
signals:
void sentTestPrivate();
public slots:
void test() { appendPlainText("Test signal received by TextView."); }
void testArgs(const QString &message) { appendPlainText(message); }
private slots:
void testPrivate() {}
};
#endif // KLIPS_TEXTVIEW_H

11
dotnet/README.md Normal file
View File

@@ -0,0 +1,11 @@
# dotnet
```bash
shaunrd0/klips/dotnet/
├── sitemap # Custom library to generate sitemaps
├── testing # General .NET practice
└── README.md
```
All of these projects were created with the `dotnet` CLI on Linux (Kubuntu 20.04).
They have not been tested on any other platform.

454
dotnet/sitemap/.gitignore vendored Normal file
View File

@@ -0,0 +1,454 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET
project.lock.json
project.fragment.lock.json
artifacts/
# Tye
.tye/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
##
## Visual studio for Mac
##
# globs
Makefile.in
*.userprefs
*.usertasks
config.make
config.status
aclocal.m4
install-sh
autom4te.cache/
*.tar.gz
tarballs/
test-results/
# Mac bundle stuff
*.dmg
*.app
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# JetBrains Rider
.idea/
*.sln.iml
##
## Visual Studio Code
##
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

View File

@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\SiteMapLibrary\SiteMapLibrary.csproj" />
</ItemGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,12 @@
using SiteMapLibrary;
// Create an XmlManager to use for generating our sitemap; Provide a file path (and optional Xml settings; See ctor)
var mgr = new XmlManager("/home/kapper/Code/klips/dotnet/sitemap/ConsoleApp/TestFiles/sitemap.xml");
// If we want to output the sitemap to the console, instead of saving to a file
// var mgr = new XmlManager("Console.Out");
// Provide a base URL to start crawling, an XmlManager, and a Regex pattern to use for matching URLs while crawling
using SiteMap siteMap = new SiteMap("https://knoats.com", mgr,
new("(http?s://knoats.com(?!.*/dist/|.*/settings/|.*/register/|.*/login/|.*/uploads/|.*/export/|.*/search?).*?(?=\"))"));
// Start crawling; When this returns, we have visited all found URLs and wrote them to our sitemap
await siteMap.Crawl();

View File

@@ -0,0 +1,9 @@
User-agent: *
Disallow:
Disallow: /dist/
Disallow: /settings/
Disallow: /register/
Disallow: /login/
Disallow: /uploads/
Disallow: /export/
Disallow: /search?

View File

@@ -0,0 +1,975 @@
<?xml version="1.0" encoding="utf-8"?>
<urlset>
<url>
<loc>https://knoats.com</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/tags</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/login</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/shelves</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/c</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/register</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/pi</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/vim</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/git</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/docker</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/ansible</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/security</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/password/email</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/bookstack</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/user/shaun-reed</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/c?shelf=2</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/javascript</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/blockchain</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/networking</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/vim?shelf=1</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/vim?shelf=2</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/git?shelf=2</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/shelves/containers</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/c/page/basics</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/shelves/programming</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/docker?shelf=3</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/docker?shelf=1</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/c/page/classes</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/vim/page/notes</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/ansible?shelf=1</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/git/page/basics</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/game-development</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/pages/recently-updated</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/security?shelf=1</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/bookstack?shelf=3</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/git/chapter/usage</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/networking?shelf=3</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/networking?shelf=1</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/docker/page/gitlab</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/docker/page/shlink</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin?shelf=3</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin?shelf=1</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/git/page/submodules</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/i3</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/networking/page/dns</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/docker/page/heimdall</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/pi/page/installation</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/c-s68/page/dotnet-cli</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/c/page/multithreading</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/grub</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/javascript/page/webgl</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/arch</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/networking/page/tcpip</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/networking/page/nginx</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/link/30#bkmrk-configure-ssl</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/security/page/fail2ban</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/networking/page/apache</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/docker/page/dockerfile</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/git/page/authentication</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/debian</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/networking/page/tcp-udp</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/pages/recently-updated?page=2</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/pages/recently-updated?page=3</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/pages/recently-updated?page=4</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/pages/recently-updated?page=5</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/pi/chapter/magic-mirror</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/pages/recently-updated?page=1</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/self-hosted-applications</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/blockchain/page/solidity</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/c/page/building-projects</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/vim/page/configuring-vim</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/chapter/bash</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/crontab</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/yakuake</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/bookstack/chapter/knoats</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/examples</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/xps-9310</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/wireless</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/security/page/ossec-rules</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/networking/page/osi-model</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/pi/chapter/backup-scripts</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/tunneling</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/networking/page/subnetting</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/bluetooth</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/ansible/page/creating-roles</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/virtualbox</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/security/chapter/monitoring</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/networking/chapter/protocols</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/pi/page/magic-mirror-modules</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/git/page/software-development</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/boot-process</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/shelves/linux-server-administration</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/game-development/page/prefabs</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/networking/page/proxy-servers</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/bookstack/chapter/development</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/game-development/chapter/unity</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/bash-profiles</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/configure-ftp</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/chapter/interfaces</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/audio-devices</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/security/page/server-checklist</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/security/page/devsec-baselines</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/networking/chapter/web-servers</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/bookstack/chapter/installation</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/game-development/page/scripting</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/game-development/page/shortcuts</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/ansible/page/creating-playbooks</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/system-sensors</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/self-hosted-applications?shelf=3</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/swap-allocation</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/getting-started</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/chapter/system-admin</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/server-hostname</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/disk-management</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/pi/page/staging-configs-to-a-usb</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/game-development/page/linux-setup</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/git/page/pushing-merging-branches</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/systemd-services</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/chapter/distributions</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/chapter/customization</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/installing-fonts</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/security/page/virtualbox-networks</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/security/page/ossec-ubuntu-server</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/security/chapter/server-hardening</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/kernel-management</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/ansible/page/managing-remote-hosts</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/configure-postfix</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/self-hosted-applications/page/hexo</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/mount-google-drive</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/self-hosted-applications/page/gitea</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/enabling-google-2fa</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/user-administration</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/unattended-upgrades</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/self-hosted-applications/page/jekyll</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/bookstack/page/welcome-to-knoats-432</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/linux-on-chromebooks</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/game-development/page/post-processing</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/chapter/ssh-configuration</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/bookstack/page/exploring-the-database</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/game-development/page/project-settings</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/game-development/page/new-input-system</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/bookstack/page/bookstack-configuration</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/docker/chapter/docker-compose-services</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/game-development/chapter/unreal-engine-4</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/networking/page/certbot-ssl-certificates</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/yubikey-ssh-authentication</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/self-hosted-applications/page/read-the-docs</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/bookstack/page/backup-bookstack-using-docker</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/game-development/page/gameplay-ability-system</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/synchronizing-time-using-ntp</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/bookstack/page/bookstack-using-docker-compose</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/self-hosted-applications/chapter/url-shortners</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/bookstack/page/updating-bookstack-using-docker</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/configuring-sshd-authentication</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/self-hosted-applications/chapter/site-generators</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/vim/page/configuring-vim#bkmrk-unicode.vim-plugin</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/game-development/page/retarget-skeleton-animations</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/self-hosted-applications/page/mame-web-application</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/linux-admin/page/configuring-multi-boot-filesystems</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://knoats.com/books/self-hosted-applications/chapter/documentation-generators</loc>
<lastmod>2022-5-4</lastmod>
<changefreq>daily</changefreq>
<priority>0.5</priority>
</url>
</urlset>

45
dotnet/sitemap/README.md Normal file
View File

@@ -0,0 +1,45 @@
Sitemap generator I created while learning some C#.
Example of using the library is in `ConsoleApp/Program.cs`, files used for testing are in `ConsoleApp/TestFiles/`
`ConsoleApp/TestFiles/sitemap.xml` currently contains the sitemap for my website.
If we run the console application with a different URL that targets this same file, the file will be overwritten with the new sitemap.
There is no need to delete or recreate files manually.
I plan to check for a `robots.txt` while generating sitemaps to prevent crawling pages that aren't useful.
For now there is no use for a `robots.txt`, the `SiteMap.Crawl()` function visits the URL provided to the `SiteMap` constructor.
Regex is used to check the visited page and match URLs with the same base domain, the URLS found are logged for the crawler to visit.
Each time we finish collecting URLS on a page, we move to the next URL in the queue and repeat this process.
Once we finish crawling all URLs, an XML sitemap is generated where the URLs are sorted by their length.
I used [sitemaps.org - XML Format](https://www.sitemaps.org/protocol.html) to determine the proper formatting for the sitemap.
For now, since the web application I used for testing does not respond with [Last-Modified](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified) in the HTTP header, the last modified time is set to the date the sitemap was generated.
The `priority` fields are all set to the default value indicated on sitemaps.org, which is `0.5`.
This is to avoid confusing crawlers with a huge list of 'top-priority' pages to crawl.
All `changefreq` fields of the sitemap are marked as `daily`.
The primary motivation for this project was learning about unmanaged resources in C#, and trying out the [Dispose Pattern](https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose?redirectedfrom=MSDN#implement-the-dispose-pattern) for myself.
If someone reading this were to find a problem with the way I handled disposing of the `HttpClient` in the `SiteMap` class, feel free to let me know :) Creating an issue, PR, or sending an email is all acceptable.
### Future plans
* Parse `robots.txt` to avoid crawling pages that are not desired
* Test the generator with an application that serves `LastModified` date; Use it if available
* Set `priority` in a more useful way, or allow some form of customization of the way this is handled.
* Set `changefreq` in a more useful way, or allow some form of customization of the way this is handled.
* Generate a regex pattern to match, if one is not provided
For now, the general use of this library is seen in the example below.
```C#
using SiteMapLibrary;
// Create an XmlManager to use for generating our sitemap; Provide a file path (and optional Xml settings; See ctor)
var mgr = new XmlManager("/home/kapper/Code/klips/dotnet/sitemap/ConsoleApp/TestFiles/sitemap.xml");
// If we want to output the sitemap to the console, instead of saving to a file
// var mgr = new XmlManager("Console.Out");
// Provide a base URL to start crawling, an XmlManager, and a Regex pattern to use for matching URLs while crawling
using SiteMap siteMap = new SiteMap("https://knoats.com", mgr,
new("(http?s://knoats.com(?!.*/dist/|.*/settings/|.*/register/|.*/login/|.*/uploads/|.*/export/|.*/search?).*?(?=\"))"));
// Start crawling; When this returns, we have visited all found URLs and wrote them to our sitemap
await siteMap.Crawl();
```

View File

@@ -0,0 +1,114 @@
using System.Text.RegularExpressions;
namespace SiteMapLibrary;
public class SiteMap : IDisposable
{
private HttpClient _client;
private HashSet<string> _foundUrls;
private HashSet<string> _visitedUrls;
private Queue<string> _visitQueue;
private bool _disposed = false;
private XmlManager XmlManager { get; set; }
public string? Url { get; private set; }
public Regex Regexp { get; set; }
public SiteMap(string url, string savepath, Regex pattern)
{
Url = url;
_client = new HttpClient();
_foundUrls = new HashSet<string>();
_visitedUrls = new HashSet<string>();
_visitQueue = new Queue<string>();
Regexp = pattern;
XmlManager = new XmlManager(savepath);
}
public SiteMap(string url, XmlManager mgr, Regex pattern)
{
_client = new HttpClient();
_foundUrls = new HashSet<string>();
_visitedUrls = new HashSet<string>();
_visitQueue = new Queue<string>();
Regexp = pattern;
Url = url;
XmlManager = mgr;
}
public async Task Crawl()
{
while (Url != null)
{
_visitedUrls.Add(Url);
using var content = await _client.GetAsync(Url);
if (!content.IsSuccessStatusCode)
{
Console.WriteLine($"{content.StatusCode} on url: {Url}");
NextUrl();
continue;
}
var m = Regexp.Match(await content.Content.ReadAsStringAsync());
while (m.Success)
{
foreach (Group group in m.Groups)
{
if (_foundUrls.Add(group.Value))
{
Console.WriteLine(group.Value);
// Console.WriteLine(content.Content.Headers.LastModified);
if (!_visitedUrls.Contains(group.Value) && !_visitQueue.Contains(group.Value))
{
_visitQueue.Enqueue(group.Value);
}
}
}
m = m.NextMatch();
}
NextUrl();
content.Dispose();
}
WriteXml();
}
private void WriteXml()
{
List<string> urls = new List<string>(_visitedUrls.OrderBy(k => k.Length).ToArray());
foreach (string url in urls)
{
XmlManager.AddUrl(url);
}
XmlManager.Save();
}
private void NextUrl()
{
if (_visitQueue.Count == 0)
{
Url = null;
return;
}
Url = _visitQueue.Dequeue();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_client.Dispose();
}
_disposed = true;
}
}
}

View File

@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>SiteMap</RootNamespace>
</PropertyGroup>
</Project>

Some files were not shown because too many files have changed in this diff Show More