From 168c9a009eb68697c1b8328cfbf3a1a6ccae48f6 Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 29 Dec 2020 22:52:02 +0800 Subject: import(life): ... --- works/life/cpp-practicum/main.cpp | 281 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 works/life/cpp-practicum/main.cpp (limited to 'works/life/cpp-practicum/main.cpp') diff --git a/works/life/cpp-practicum/main.cpp b/works/life/cpp-practicum/main.cpp new file mode 100644 index 0000000..0f90b74 --- /dev/null +++ b/works/life/cpp-practicum/main.cpp @@ -0,0 +1,281 @@ +#include "Base.hpp" +#include "StringUtil.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +inline std::u16string_view ToUtf16View(const std::wstring &str) { + return std::u16string_view(reinterpret_cast(str.c_str()), + str.size()); +} + +inline std::wstring_view ToWStringView(std::u16string_view str) { + return std::wstring_view(reinterpret_cast(str.data()), + str.size()); +} + +class SerializationException : public std::runtime_error { +public: + using runtime_error::runtime_error; +}; + +std::vector SplitByVerticalLine(std::string_view str, + int field_count) { + std::vector fields; + gsl::index current_position = 0; + + for (int i = 0; i < field_count - 1; i++) { + auto pos = str.find('|', current_position); + if (pos == std::string::npos) { + throw SerializationException("Failed to find next splitter('|')."); + } + fields.push_back(str.substr(current_position, pos - current_position)); + current_position = pos + 1; + } + fields.push_back(str.substr(current_position)); + + return fields; +} + +class Book final { +public: + Book() = default; + + Book(std::u16string isbn, std::u16string title, std::u16string type, + std::u16string author, std::u16string press, int stock_count) + : isbn_(std::move(isbn)), title_(std::move(title)), + type_(std::move(type)), author_(std::move(author)), + press_(std::move(press)), stock_count_(stock_count) {} + + CRU_DEFAULT_COPY(Book) + CRU_DEFAULT_MOVE(Book) + + ~Book() = default; + +public: + std::u16string GetIsbn() const { return isbn_; } + void SetIsbn(std::u16string isbn) { isbn_ = std::move(isbn); } + + std::u16string GetTitle() const { return title_; } + void SetTitle(std::u16string title) { title_ = std::move(title); } + + std::u16string GetType() const { return type_; } + void SetType(std::u16string type) { type_ = std::move(type); } + + std::u16string GetAuthor() const { return author_; } + void SetAuthor(std::u16string author) { author_ = std::move(author); } + + std::u16string GetPress() const { return press_; } + void SetPress(std::u16string press) { press_ = std::move(press); } + + int GetStockCount() const { return stock_count_; } + void SetStockCount(int stock_count) { stock_count_ = stock_count; } + +private: + std::u16string isbn_; + std::u16string title_; + std::u16string type_; + std::u16string author_; + std::u16string press_; + int stock_count_; +}; + +std::istream &operator>>(std::istream &left, Book &right) { + std::string line; + std::getline(left, line); + + std::vector fields = SplitByVerticalLine(line, 6); + + right.SetIsbn(cru::ToUtf16(fields[0])); + right.SetTitle(cru::ToUtf16(fields[1])); + right.SetType(cru::ToUtf16(fields[2])); + right.SetAuthor(cru::ToUtf16(fields[3])); + right.SetPress(cru::ToUtf16(fields[4])); + right.SetStockCount(std::stoi(std::string(fields[5]))); + + return left; +} + +std::ostream &operator<<(std::ostream &left, const Book &right) { + left << cru::ToUtf8(right.GetIsbn()) << '|' << cru::ToUtf8(right.GetTitle()) + << '|' << cru::ToUtf8(right.GetType()) << '|' + << cru::ToUtf8(right.GetAuthor()) << '|' << cru::ToUtf8(right.GetPress()) + << '|' << right.GetStockCount(); + return left; +} + +void PrettyPrint(std::wostream &stream, const Book &book) { + stream << L"ISBN: " << ToWStringView(book.GetIsbn()) << L"\n"; + stream << L"标题: " << ToWStringView(book.GetTitle()) << L"\n"; + stream << L"类型: " << ToWStringView(book.GetType()) << L"\n"; + stream << L"作者: " << ToWStringView(book.GetAuthor()) << L"\n"; + stream << L"出版社: " << ToWStringView(book.GetPress()) << L"\n"; + stream << L"库存: " << book.GetStockCount() << L"\n"; +} + +class Vendor final { +public: + Vendor() = default; + Vendor(int id, std::u16string name, std::u16string type, + std::u16string address, std::u16string phone) + : id_(id), name_(std::move(name)), type_(std::move(type)), + address_(std::move(address)), phone_(std::move(phone)) {} + + CRU_DEFAULT_COPY(Vendor) + CRU_DEFAULT_MOVE(Vendor) + + ~Vendor() = default; + +public: + int GetId() const { return id_; } + void SetId(int id) { id_ = id; } + + std::u16string GetName() const { return name_; } + void SetName(std::u16string name) { name_ = std::move(name); } + + std::u16string GetType() const { return type_; } + void SetType(std::u16string type) { type_ = std::move(type); } + + std::u16string GetAddress() const { return address_; } + void SetAddress(std::u16string address) { address_ = std::move(address); } + + std::u16string GetPhone() const { return phone_; } + void SetPhone(std::u16string phone) { phone_ = std::move(phone); } + +private: + int id_; + std::u16string name_; + std::u16string type_; + std::u16string address_; + std::u16string phone_; +}; + +std::istream &operator>>(std::istream &left, Vendor &right) { + std::string line; + std::getline(left, line); + + std::vector fields = SplitByVerticalLine(line, 5); + + right.SetId(std::stoi(std::string(fields[0]))); + right.SetName(cru::ToUtf16(fields[1])); + right.SetType(cru::ToUtf16(fields[2])); + right.SetAddress(cru::ToUtf16(fields[3])); + right.SetPhone(cru::ToUtf16(fields[4])); + + return left; +} + +std::ostream &operator<<(std::ostream &left, const Vendor &right) { + left << right.GetId() << '|' << cru::ToUtf8(right.GetName()) << '|' + << cru::ToUtf8(right.GetType()) << '|' << cru::ToUtf8(right.GetAddress()) + << '|' << cru::ToUtf8(right.GetPhone()); + return left; +} + +class Record final { +public: + Record(); + + CRU_DEFAULT_COPY(Record); + CRU_DEFAULT_MOVE(Record); + + ~Record() = default; + +public: + void WriteTo(std::ostream &stream); + void ReadFrom(std::istream &stream); + + const std::vector &GetBooks() const { return books_; } + const std::vector &GetVendors() const { return vendors_; } + + // TODO: Implementation + std::optional FindBookByIsbn(std::u16string_view isbn); + + // TODO: Implementation + void RemoveBookByIsbn(std::u16string_view isbn); + +private: + std::vector books_; + std::vector vendors_; +}; + +void Record::WriteTo(std::ostream &stream) { + stream << books_.size() << ' ' << vendors_.size() << '\n'; + for (const auto &book : books_) { + stream << book << '\n'; + } + for (const auto &vendor : vendors_) { + stream << vendor << '\n'; + } +} + +void Record::ReadFrom(std::istream &stream) { + books_.clear(); + vendors_.clear(); + int book_count, vendor_count; + stream >> book_count >> vendor_count; + stream >> std::ws; + for (int i = 0; i < book_count; i++) { + Book book; + stream >> book; + books_.push_back(std::move(book)); + } + for (int i = 0; i < vendor_count; i++) { + Vendor vendor; + stream >> vendor; + vendors_.push_back(std::move(vendor)); + } +} + +int main() { + Record record; + + while (true) { + std::wcout << L"1. 查询 2. 添加 0. 退出\n"; + int choice = 0; + std::wcin >> choice; + if (choice == 1) { + std::wcout + << L"1. 图书 2. 供应商\n输入数字选择操作,其他任意字符将退出程序。\n"; + choice = 0; + std::wcin >> choice; + if (choice == 1) { + std::wcout << L"请输入图书编号:\n"; + std::wstring isbn; + std::wcin >> isbn; + auto find_result = record.FindBookByIsbn(ToUtf16View(isbn)); + if (find_result) { + PrettyPrint(std::wcout, *find_result); + std::wcout << L"0. 返回主菜单 1. 修改 2. 删除\n"; + choice = 0; + std::wcin >> choice; + if (choice == 1) { + // TODO: + } else if (choice == 2) { + record.RemoveBookByIsbn(ToUtf16View(isbn)); + std::wcout << L"删除成功。\n"; + } + } else { + std::wcout << L"该书不存在。\n"; + } + } else if (choice == 2) { + + } else { + return 0; + } + + } else if (choice == 2) { + + } else { + return 0; + } + } + + return 0; +} -- cgit v1.2.3 From e0964bbc4f66e7d193abd249213c9cf762fd0294 Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 30 Dec 2020 15:37:39 +0800 Subject: import(life): ... --- works/life/cpp-practicum/Base.hpp | 37 +--- works/life/cpp-practicum/Book.cpp | 36 ++++ works/life/cpp-practicum/Book.hpp | 51 ++++++ works/life/cpp-practicum/CMakeLists.txt | 21 ++- works/life/cpp-practicum/Record.cpp | 38 ++++ works/life/cpp-practicum/Record.hpp | 36 ++++ works/life/cpp-practicum/StringUtil.cpp | 305 -------------------------------- works/life/cpp-practicum/StringUtil.hpp | 148 ---------------- works/life/cpp-practicum/Vendor.cpp | 33 ++++ works/life/cpp-practicum/Vendor.hpp | 45 +++++ works/life/cpp-practicum/main.cpp | 297 +++---------------------------- 11 files changed, 286 insertions(+), 761 deletions(-) create mode 100644 works/life/cpp-practicum/Book.cpp create mode 100644 works/life/cpp-practicum/Book.hpp create mode 100644 works/life/cpp-practicum/Record.cpp create mode 100644 works/life/cpp-practicum/Record.hpp delete mode 100644 works/life/cpp-practicum/StringUtil.cpp delete mode 100644 works/life/cpp-practicum/StringUtil.hpp create mode 100644 works/life/cpp-practicum/Vendor.cpp create mode 100644 works/life/cpp-practicum/Vendor.hpp (limited to 'works/life/cpp-practicum/main.cpp') diff --git a/works/life/cpp-practicum/Base.hpp b/works/life/cpp-practicum/Base.hpp index f26b81f..bdf87dd 100644 --- a/works/life/cpp-practicum/Base.hpp +++ b/works/life/cpp-practicum/Base.hpp @@ -1,15 +1,8 @@ #pragma once -#include #include -// #include #include -// Now we have no gsl but we need gsl::index -namespace gsl { -using index = std::ptrdiff_t; -} - #define CRU_UNUSED(entity) static_cast(entity); #define CRU__CONCAT(a, b) a##b @@ -31,33 +24,7 @@ using index = std::ptrdiff_t; classname(classname &&) = delete; \ classname &operator=(classname &&) = delete; -namespace cru { -class Object { +class SerializationException : public std::runtime_error { public: - Object() = default; - CRU_DEFAULT_COPY(Object) - CRU_DEFAULT_MOVE(Object) - virtual ~Object() = default; -}; - -struct Interface { - Interface() = default; - CRU_DELETE_COPY(Interface) - CRU_DELETE_MOVE(Interface) - virtual ~Interface() = default; + using runtime_error::runtime_error; }; - -[[noreturn]] inline void UnreachableCode() { std::terminate(); } - -using Index = gsl::index; - -// https://www.boost.org/doc/libs/1_54_0/doc/html/hash/reference.html#boost.hash_combine -template inline void hash_combine(std::size_t &s, const T &v) { - std::hash h; - s ^= h(v) + 0x9e3779b9 + (s << 6) + (s >> 2); -} - -#define CRU_DEFINE_CLASS_LOG_TAG(tag) \ -private: \ - constexpr static std::u16string_view log_tag = tag; -} // namespace cru diff --git a/works/life/cpp-practicum/Book.cpp b/works/life/cpp-practicum/Book.cpp new file mode 100644 index 0000000..bf67d7c --- /dev/null +++ b/works/life/cpp-practicum/Book.cpp @@ -0,0 +1,36 @@ +#include "Book.hpp" + +#include + +QTextStream &operator>>(QTextStream &left, Book &right) { + auto line = left.readLine(); + + auto fields = line.split(QChar('|')); + + if (fields.size() != 6) { + throw SerializationException("The line has not 6 parts."); + } + + right.SetIsbn(fields[0].toStdU16String()); + right.SetTitle(fields[1].toStdU16String()); + right.SetType(fields[2].toStdU16String()); + right.SetAuthor(fields[3].toStdU16String()); + right.SetPress(fields[4].toStdU16String()); + + bool ok; + auto stock_count = fields[5].toInt(&ok); + if (!ok) { + throw SerializationException("Part 6 is not a number."); + } + + right.SetStockCount(stock_count); + + return left; +} + +QTextStream &operator<<(QTextStream &left, const Book &right) { + left << right.GetIsbn() << '|' << right.GetTitle() << '|' << right.GetType() + << '|' << right.GetAuthor() << '|' << right.GetPress() << '|' + << right.GetStockCount(); + return left; +} diff --git a/works/life/cpp-practicum/Book.hpp b/works/life/cpp-practicum/Book.hpp new file mode 100644 index 0000000..2fb1123 --- /dev/null +++ b/works/life/cpp-practicum/Book.hpp @@ -0,0 +1,51 @@ +#pragma once +#include "Base.hpp" + +#include +#include + +class Book final { +public: + Book() = default; + + Book(std::u16string isbn, std::u16string title, std::u16string type, + std::u16string author, std::u16string press, int stock_count) + : isbn_(std::move(isbn)), title_(std::move(title)), + type_(std::move(type)), author_(std::move(author)), + press_(std::move(press)), stock_count_(stock_count) {} + + CRU_DEFAULT_COPY(Book) + CRU_DEFAULT_MOVE(Book) + + ~Book() = default; + +public: + std::u16string GetIsbn() const { return isbn_; } + void SetIsbn(std::u16string isbn) { isbn_ = std::move(isbn); } + + std::u16string GetTitle() const { return title_; } + void SetTitle(std::u16string title) { title_ = std::move(title); } + + std::u16string GetType() const { return type_; } + void SetType(std::u16string type) { type_ = std::move(type); } + + std::u16string GetAuthor() const { return author_; } + void SetAuthor(std::u16string author) { author_ = std::move(author); } + + std::u16string GetPress() const { return press_; } + void SetPress(std::u16string press) { press_ = std::move(press); } + + int GetStockCount() const { return stock_count_; } + void SetStockCount(int stock_count) { stock_count_ = stock_count; } + +private: + std::u16string isbn_; + std::u16string title_; + std::u16string type_; + std::u16string author_; + std::u16string press_; + int stock_count_; +}; + +QTextStream &operator>>(QTextStream &left, Book &right); +QTextStream &operator<<(QTextStream &left, const Book &right); diff --git a/works/life/cpp-practicum/CMakeLists.txt b/works/life/cpp-practicum/CMakeLists.txt index fbf1c13..7958850 100644 --- a/works/life/cpp-practicum/CMakeLists.txt +++ b/works/life/cpp-practicum/CMakeLists.txt @@ -1,3 +1,6 @@ +set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../vcpkg/scripts/buildsystems/vcpkg.cmake + CACHE STRING "Vcpkg toolchain file") + cmake_minimum_required(VERSION 3.14) project(CppPraticum) @@ -11,4 +14,20 @@ if (MSVC) add_compile_options(/utf-8 /W4 /WX) endif() -add_executable(main main.cpp Base.hpp StringUtil.hpp StringUtil.cpp) +find_package(Qt5Core CONFIG REQUIRED) +find_package(Qt5Gui CONFIG REQUIRED) +find_package(Qt5Widgets CONFIG REQUIRED) + +add_executable(main + Base.hpp + + Book.hpp + Book.cpp + Vendor.hpp + Vendor.cpp + Record.hpp + Record.cpp + + main.cpp +) +target_link_libraries(main PRIVATE Qt5::Core Qt5::Widgets Qt5::Gui) diff --git a/works/life/cpp-practicum/Record.cpp b/works/life/cpp-practicum/Record.cpp new file mode 100644 index 0000000..1041b23 --- /dev/null +++ b/works/life/cpp-practicum/Record.cpp @@ -0,0 +1,38 @@ +#include "Record.hpp" + +void Record::WriteTo(QFile file) { + file.open(QFile::ReadWrite | QFile::Text | QFile::Truncate); + QTextStream stream(&file); + stream.setCodec("UTF-8"); + + stream << books_.size() << ' ' << vendors_.size() << '\n'; + for (const auto &book : books_) { + stream << book << '\n'; + } + for (const auto &vendor : vendors_) { + stream << vendor << '\n'; + } +} + +void Record::ReadFrom(QFile file) { + file.open(QFile::ReadOnly | QFile::Text); + QTextStream stream(&file); + stream.setCodec("UTF-8"); + + books_.clear(); + vendors_.clear(); + + int book_count, vendor_count; + stream >> book_count >> vendor_count; + stream.skipWhiteSpace(); + for (int i = 0; i < book_count; i++) { + Book book; + stream >> book; + books_.push_back(std::move(book)); + } + for (int i = 0; i < vendor_count; i++) { + Vendor vendor; + stream >> vendor; + vendors_.push_back(std::move(vendor)); + } +} diff --git a/works/life/cpp-practicum/Record.hpp b/works/life/cpp-practicum/Record.hpp new file mode 100644 index 0000000..e39e626 --- /dev/null +++ b/works/life/cpp-practicum/Record.hpp @@ -0,0 +1,36 @@ +#pragma once +#include "Base.hpp" + +#include "Book.hpp" +#include "Vendor.hpp" + +#include +#include +#include + +class Record final { +public: + Record(); + + CRU_DEFAULT_COPY(Record); + CRU_DEFAULT_MOVE(Record); + + ~Record() = default; + +public: + void WriteTo(QFile file); + void ReadFrom(QFile file); + + const std::vector &GetBooks() const { return books_; } + const std::vector &GetVendors() const { return vendors_; } + + // TODO: Implementation + std::optional FindBookByIsbn(std::u16string_view isbn); + + // TODO: Implementation + void RemoveBookByIsbn(std::u16string_view isbn); + +private: + std::vector books_; + std::vector vendors_; +}; diff --git a/works/life/cpp-practicum/StringUtil.cpp b/works/life/cpp-practicum/StringUtil.cpp deleted file mode 100644 index e6cd377..0000000 --- a/works/life/cpp-practicum/StringUtil.cpp +++ /dev/null @@ -1,305 +0,0 @@ -#include "StringUtil.hpp" -#include "Base.hpp" - -namespace cru { -namespace { -template -inline std::enable_if_t, ReturnType> -ExtractBits(UInt n) { - return static_cast(n & ((1u << number_of_bit) - 1)); -} -} // namespace - -CodePoint Utf8NextCodePoint(std::string_view str, Index current, - Index *next_position) { - CodePoint result; - - if (current >= static_cast(str.length())) { - result = k_invalid_code_point; - } else { - const auto cu0 = static_cast(str[current++]); - - auto read_next_folowing_code = [&str, ¤t]() -> CodePoint { - if (current == static_cast(str.length())) - throw TextEncodeException( - "Unexpected end when read continuing byte of multi-byte code " - "point."); - - const auto u = static_cast(str[current]); - if (!(u & (1u << 7)) || (u & (1u << 6))) { - throw TextEncodeException( - "Unexpected bad-format (not 0b10xxxxxx) continuing byte of " - "multi-byte code point."); - } - - return ExtractBits(str[current++]); - }; - - if ((1u << 7) & cu0) { - if ((1u << 6) & cu0) { // 2~4-length code point - if ((1u << 5) & cu0) { // 3~4-length code point - if ((1u << 4) & cu0) { // 4-length code point - if (cu0 & (1u << 3)) { - throw TextEncodeException( - "Unexpected bad-format begin byte (not 0b11110xxx) of 4-byte" - "code point."); - } - - const CodePoint s0 = ExtractBits(cu0) - << (6 * 3); - const CodePoint s1 = read_next_folowing_code() << (6 * 2); - const CodePoint s2 = read_next_folowing_code() << 6; - const CodePoint s3 = read_next_folowing_code(); - result = s0 + s1 + s2 + s3; - } else { // 3-length code point - const CodePoint s0 = ExtractBits(cu0) - << (6 * 2); - const CodePoint s1 = read_next_folowing_code() << 6; - const CodePoint s2 = read_next_folowing_code(); - result = s0 + s1 + s2; - } - } else { // 2-length code point - const CodePoint s0 = ExtractBits(cu0) - << 6; - const CodePoint s1 = read_next_folowing_code(); - result = s0 + s1; - } - } else { - throw TextEncodeException( - "Unexpected bad-format (0b10xxxxxx) begin byte of a code point."); - } - } else { - result = static_cast(cu0); - } - } - - if (next_position != nullptr) - *next_position = current; - return result; -} - -CodePoint Utf16NextCodePoint(std::u16string_view str, Index current, - Index *next_position) { - CodePoint result; - - if (current >= static_cast(str.length())) { - result = k_invalid_code_point; - } else { - const auto cu0 = str[current++]; - - if (!IsUtf16SurrogatePairCodeUnit(cu0)) { // 1-length code point - result = static_cast(cu0); - } else if (IsUtf16SurrogatePairLeading(cu0)) { // 2-length code point - if (current >= static_cast(str.length())) { - throw TextEncodeException( - "Unexpected end when reading second code unit of surrogate pair."); - } - const auto cu1 = str[current++]; - - if (!IsUtf16SurrogatePairTrailing(cu1)) { - throw TextEncodeException( - "Unexpected bad-range second code unit of surrogate pair."); - } - - const auto s0 = ExtractBits(cu0) << 10; - const auto s1 = ExtractBits(cu1); - - result = s0 + s1 + 0x10000; - - } else { - throw TextEncodeException( - "Unexpected bad-range first code unit of surrogate pair."); - } - } - - if (next_position != nullptr) - *next_position = current; - return result; -} - -CodePoint Utf16PreviousCodePoint(std::u16string_view str, Index current, - Index *previous_position) { - CodePoint result; - if (current <= 0) { - result = k_invalid_code_point; - } else { - const auto cu0 = str[--current]; - - if (!IsUtf16SurrogatePairCodeUnit(cu0)) { // 1-length code point - result = static_cast(cu0); - } else if (IsUtf16SurrogatePairTrailing(cu0)) { // 2-length code point - if (current <= 0) { - throw TextEncodeException( - "Unexpected end when reading first code unit of surrogate pair."); - } - const auto cu1 = str[--current]; - - if (!IsUtf16SurrogatePairLeading(cu1)) { - throw TextEncodeException( - "Unexpected bad-range first code unit of surrogate pair."); - } - - const auto s0 = ExtractBits(cu1) << 10; - const auto s1 = ExtractBits(cu0); - - result = s0 + s1 + 0x10000; - - } else { - throw TextEncodeException( - "Unexpected bad-range second code unit of surrogate pair."); - } - } - - if (previous_position != nullptr) - *previous_position = current; - return result; -} - -void Utf8EncodeCodePointAppend(CodePoint code_point, std::string &str) { - auto write_continue_byte = [&str](std::uint8_t byte6) { - str.push_back((1u << 7) + (((1u << 6) - 1) & byte6)); - }; - - if (code_point >= 0 && code_point <= 0x007F) { - str.push_back(static_cast(code_point)); - } else if (code_point >= 0x0080 && code_point <= 0x07FF) { - std::uint32_t unsigned_code_point = code_point; - str.push_back(static_cast(ExtractBits( - (unsigned_code_point >> 6)) + - 0b11000000)); - write_continue_byte( - ExtractBits(unsigned_code_point)); - } else if (code_point >= 0x0800 && code_point <= 0xFFFF) { - std::uint32_t unsigned_code_point = code_point; - str.push_back(static_cast(ExtractBits( - (unsigned_code_point >> (6 * 2))) + - 0b11100000)); - write_continue_byte( - ExtractBits(unsigned_code_point >> 6)); - write_continue_byte( - ExtractBits(unsigned_code_point)); - } else if (code_point >= 0x10000 && code_point <= 0x10FFFF) { - std::uint32_t unsigned_code_point = code_point; - str.push_back(static_cast(ExtractBits( - (unsigned_code_point >> (6 * 3))) + - 0b11110000)); - write_continue_byte(ExtractBits( - unsigned_code_point >> (6 * 2))); - write_continue_byte( - ExtractBits(unsigned_code_point >> 6)); - write_continue_byte( - ExtractBits(unsigned_code_point)); - } else { - throw TextEncodeException("Code point out of range."); - } -} - -void Utf16EncodeCodePointAppend(CodePoint code_point, std::u16string &str) { - if ((code_point >= 0 && code_point <= 0xD7FF) || - (code_point >= 0xE000 && code_point <= 0xFFFF)) { - str.push_back(static_cast(code_point)); - } else if (code_point >= 0x10000 && code_point <= 0x10FFFF) { - std::uint32_t u = code_point - 0x10000; - str.push_back(static_cast( - ExtractBits(u >> 10) + 0xD800u)); - str.push_back(static_cast( - ExtractBits(u) + 0xDC00u)); - } else { - throw TextEncodeException("Code point out of range."); - } -} - -std::string ToUtf8(std::u16string_view s) { - std::string result; - for (CodePoint cp : Utf16CodePointIterator{s}) { - Utf8EncodeCodePointAppend(cp, result); - } - return result; -} - -std::u16string ToUtf16(std::string_view s) { - std::u16string result; - for (CodePoint cp : Utf8CodePointIterator{s}) { - Utf16EncodeCodePointAppend(cp, result); - } - return result; -} - -bool Utf16IsValidInsertPosition(std::u16string_view s, gsl::index position) { - if (position < 0) - return false; - if (position > static_cast(s.size())) - return false; - if (position == 0) - return true; - if (position == static_cast(s.size())) - return true; - return !IsUtf16SurrogatePairTrailing(s[position]); -} - -gsl::index Utf16BackwardUntil(std::u16string_view str, gsl::index position, - const std::function &predicate) { - if (position <= 0) - return position; - while (true) { - gsl::index p = position; - auto c = Utf16PreviousCodePoint(str, p, &position); - if (predicate(c)) - return p; - if (c == k_invalid_code_point) - return p; - } - UnreachableCode(); -} - -gsl::index Utf16ForwardUntil(std::u16string_view str, gsl::index position, - const std::function &predicate) { - if (position >= static_cast(str.size())) - return position; - while (true) { - gsl::index p = position; - auto c = Utf16NextCodePoint(str, p, &position); - if (predicate(c)) - return p; - if (c == k_invalid_code_point) - return p; - } - UnreachableCode(); -} - -inline bool IsSpace(CodePoint c) { return c == 0x20 || c == 0xA; } - -gsl::index Utf16PreviousWord(std::u16string_view str, gsl::index position, - bool *is_space) { - if (position <= 0) - return position; - auto c = Utf16PreviousCodePoint(str, position, nullptr); - if (IsSpace(c)) { // TODO: Currently only test against 0x20(space). - if (is_space) - *is_space = true; - return Utf16BackwardUntil(str, position, - [](CodePoint c) { return !IsSpace(c); }); - } else { - if (is_space) - *is_space = false; - return Utf16BackwardUntil(str, position, IsSpace); - } -} - -gsl::index Utf16NextWord(std::u16string_view str, gsl::index position, - bool *is_space) { - if (position >= static_cast(str.size())) - return position; - auto c = Utf16NextCodePoint(str, position, nullptr); - if (IsSpace(c)) { // TODO: Currently only test against 0x20(space). - if (is_space) - *is_space = true; - return Utf16ForwardUntil(str, position, - [](CodePoint c) { return !IsSpace(c); }); - } else { - if (is_space) - *is_space = false; - return Utf16ForwardUntil(str, position, IsSpace); - } -} -} // namespace cru diff --git a/works/life/cpp-practicum/StringUtil.hpp b/works/life/cpp-practicum/StringUtil.hpp deleted file mode 100644 index d7b6cc9..0000000 --- a/works/life/cpp-practicum/StringUtil.hpp +++ /dev/null @@ -1,148 +0,0 @@ -#pragma once -#include "Base.hpp" - -#include -#include -#include - -namespace cru { -using CodePoint = std::int32_t; -constexpr CodePoint k_invalid_code_point = -1; - -class TextEncodeException : public std::runtime_error { -public: - using runtime_error::runtime_error; -}; - -inline bool IsUtf16SurrogatePairCodeUnit(char16_t c) { - return c >= 0xD800 && c <= 0xDFFF; -} - -inline bool IsUtf16SurrogatePairLeading(char16_t c) { - return c >= 0xD800 && c <= 0xDBFF; -} - -inline bool IsUtf16SurrogatePairTrailing(char16_t c) { - return c >= 0xDC00 && c <= 0xDFFF; -} - -CodePoint Utf8NextCodePoint(std::string_view str, Index current, - Index *next_position); - -CodePoint Utf16NextCodePoint(std::u16string_view str, Index current, - Index *next_position); -CodePoint Utf16PreviousCodePoint(std::u16string_view str, Index current, - Index *previous_position); - -template -using NextCodePointFunctionType = CodePoint (*)(StringType, Index, Index *); - -template NextCodePointFunction> -class CodePointIterator { -public: - using difference_type = Index; - using value_type = CodePoint; - using pointer = void; - using reference = value_type; - using iterator_category = std::forward_iterator_tag; - -public: - struct past_end_tag_t {}; - - explicit CodePointIterator(StringType string) - : string_(std::move(string)), position_(0) {} - explicit CodePointIterator(StringType string, past_end_tag_t) - : string_(std::move(string)), position_(string_.size()) {} - - CRU_DEFAULT_COPY(CodePointIterator) - CRU_DEFAULT_MOVE(CodePointIterator) - - ~CodePointIterator() = default; - -public: - StringType GetString() const { return string_; } - Index GetPosition() const { return position_; } - - bool IsPastEnd() const { - return position_ == static_cast(string_.size()); - } - -public: - CodePointIterator begin() const { return *this; } - CodePointIterator end() const { - return CodePointIterator{string_, past_end_tag_t{}}; - } - -public: - bool operator==(const CodePointIterator &other) const { - // You should compare iterator that iterate on the same string. - Expects(this->string_.data() == other.string_.data() && - this->string_.size() == other.string_.size()); - return this->position_ == other.position_; - } - bool operator!=(const CodePointIterator &other) const { - return !this->operator==(other); - } - - CodePointIterator &operator++() { - Expects(!IsPastEnd()); - Forward(); - return *this; - } - - CodePointIterator operator++(int) { - Expects(!IsPastEnd()); - CodePointIterator old = *this; - Forward(); - return old; - } - - CodePoint operator*() const { - return NextCodePointFunction(string_, position_, &next_position_cache_); - } - -private: - void Forward() { - if (next_position_cache_ > position_) { - position_ = next_position_cache_; - } else { - NextCodePointFunction(string_, position_, &position_); - } - } - -private: - StringType string_; - Index position_; - mutable Index next_position_cache_; -}; - -using Utf8CodePointIterator = - CodePointIterator; - -using Utf16CodePointIterator = - CodePointIterator; - -void Utf8EncodeCodePointAppend(CodePoint code_point, std::string &str); -void Utf16EncodeCodePointAppend(CodePoint code_point, std::u16string &str); - -std::string ToUtf8(std::u16string_view s); -std::u16string ToUtf16(std::string_view s); - -// If given s is not a valid utf16 string, return value is UD. -bool Utf16IsValidInsertPosition(std::u16string_view s, gsl::index position); - -// Return position after the character making predicate returns true or 0 if no -// character doing so. -gsl::index Utf16BackwardUntil(std::u16string_view str, gsl::index position, - const std::function &predicate); -// Return position before the character making predicate returns true or -// str.size() if no character doing so. -gsl::index Utf16ForwardUntil(std::u16string_view str, gsl::index position, - const std::function &predicate); - -gsl::index Utf16PreviousWord(std::u16string_view str, gsl::index position, - bool *is_space = nullptr); -gsl::index Utf16NextWord(std::u16string_view str, gsl::index position, - bool *is_space = nullptr); -} // namespace cru diff --git a/works/life/cpp-practicum/Vendor.cpp b/works/life/cpp-practicum/Vendor.cpp new file mode 100644 index 0000000..7544451 --- /dev/null +++ b/works/life/cpp-practicum/Vendor.cpp @@ -0,0 +1,33 @@ +#include "Vendor.hpp" + +#include + +QTextStream &operator>>(QTextStream &left, Vendor &right) { + auto line = left.readLine(); + + auto fields = line.split(QChar('|')); + + if (fields.size() != 5) { + throw SerializationException("Line has not 5 parts."); + } + + bool ok; + auto id = fields[0].toInt(&ok); + if (!ok) { + throw SerializationException("Part 1 is not a number."); + } + + right.SetId(id); + right.SetName(fields[1].toStdU16String()); + right.SetType(fields[2].toStdU16String()); + right.SetAddress(fields[3].toStdU16String()); + right.SetPhone(fields[4].toStdU16String()); + + return left; +} + +QTextStream &operator<<(QTextStream &left, const Vendor &right) { + left << right.GetId() << '|' << right.GetName() << '|' << right.GetType() + << '|' << right.GetAddress() << '|' << right.GetPhone(); + return left; +} diff --git a/works/life/cpp-practicum/Vendor.hpp b/works/life/cpp-practicum/Vendor.hpp new file mode 100644 index 0000000..24c22ee --- /dev/null +++ b/works/life/cpp-practicum/Vendor.hpp @@ -0,0 +1,45 @@ +#pragma once +#include "Base.hpp" + +#include +#include + +class Vendor final { +public: + Vendor() = default; + Vendor(int id, std::u16string name, std::u16string type, + std::u16string address, std::u16string phone) + : id_(id), name_(std::move(name)), type_(std::move(type)), + address_(std::move(address)), phone_(std::move(phone)) {} + + CRU_DEFAULT_COPY(Vendor) + CRU_DEFAULT_MOVE(Vendor) + + ~Vendor() = default; + +public: + int GetId() const { return id_; } + void SetId(int id) { id_ = id; } + + std::u16string GetName() const { return name_; } + void SetName(std::u16string name) { name_ = std::move(name); } + + std::u16string GetType() const { return type_; } + void SetType(std::u16string type) { type_ = std::move(type); } + + std::u16string GetAddress() const { return address_; } + void SetAddress(std::u16string address) { address_ = std::move(address); } + + std::u16string GetPhone() const { return phone_; } + void SetPhone(std::u16string phone) { phone_ = std::move(phone); } + +private: + int id_; + std::u16string name_; + std::u16string type_; + std::u16string address_; + std::u16string phone_; +}; + +QTextStream &operator>>(QTextStream &left, Vendor &right); +QTextStream &operator<<(QTextStream &left, const Vendor &right); diff --git a/works/life/cpp-practicum/main.cpp b/works/life/cpp-practicum/main.cpp index 0f90b74..a6c92e5 100644 --- a/works/life/cpp-practicum/main.cpp +++ b/works/life/cpp-practicum/main.cpp @@ -1,281 +1,34 @@ -#include "Base.hpp" -#include "StringUtil.hpp" +#include "qboxlayout.h" +#include "qnamespace.h" +#include +#include +#include +#include +#include +#include -#include -#include -#include -#include -#include -#include -#include -#include +int main(int argc, char *argv[]) { + QApplication application(argc, argv); -inline std::u16string_view ToUtf16View(const std::wstring &str) { - return std::u16string_view(reinterpret_cast(str.c_str()), - str.size()); -} - -inline std::wstring_view ToWStringView(std::u16string_view str) { - return std::wstring_view(reinterpret_cast(str.data()), - str.size()); -} - -class SerializationException : public std::runtime_error { -public: - using runtime_error::runtime_error; -}; - -std::vector SplitByVerticalLine(std::string_view str, - int field_count) { - std::vector fields; - gsl::index current_position = 0; - - for (int i = 0; i < field_count - 1; i++) { - auto pos = str.find('|', current_position); - if (pos == std::string::npos) { - throw SerializationException("Failed to find next splitter('|')."); - } - fields.push_back(str.substr(current_position, pos - current_position)); - current_position = pos + 1; - } - fields.push_back(str.substr(current_position)); - - return fields; -} - -class Book final { -public: - Book() = default; - - Book(std::u16string isbn, std::u16string title, std::u16string type, - std::u16string author, std::u16string press, int stock_count) - : isbn_(std::move(isbn)), title_(std::move(title)), - type_(std::move(type)), author_(std::move(author)), - press_(std::move(press)), stock_count_(stock_count) {} - - CRU_DEFAULT_COPY(Book) - CRU_DEFAULT_MOVE(Book) - - ~Book() = default; - -public: - std::u16string GetIsbn() const { return isbn_; } - void SetIsbn(std::u16string isbn) { isbn_ = std::move(isbn); } - - std::u16string GetTitle() const { return title_; } - void SetTitle(std::u16string title) { title_ = std::move(title); } - - std::u16string GetType() const { return type_; } - void SetType(std::u16string type) { type_ = std::move(type); } - - std::u16string GetAuthor() const { return author_; } - void SetAuthor(std::u16string author) { author_ = std::move(author); } - - std::u16string GetPress() const { return press_; } - void SetPress(std::u16string press) { press_ = std::move(press); } - - int GetStockCount() const { return stock_count_; } - void SetStockCount(int stock_count) { stock_count_ = stock_count; } - -private: - std::u16string isbn_; - std::u16string title_; - std::u16string type_; - std::u16string author_; - std::u16string press_; - int stock_count_; -}; - -std::istream &operator>>(std::istream &left, Book &right) { - std::string line; - std::getline(left, line); - - std::vector fields = SplitByVerticalLine(line, 6); - - right.SetIsbn(cru::ToUtf16(fields[0])); - right.SetTitle(cru::ToUtf16(fields[1])); - right.SetType(cru::ToUtf16(fields[2])); - right.SetAuthor(cru::ToUtf16(fields[3])); - right.SetPress(cru::ToUtf16(fields[4])); - right.SetStockCount(std::stoi(std::string(fields[5]))); - - return left; -} - -std::ostream &operator<<(std::ostream &left, const Book &right) { - left << cru::ToUtf8(right.GetIsbn()) << '|' << cru::ToUtf8(right.GetTitle()) - << '|' << cru::ToUtf8(right.GetType()) << '|' - << cru::ToUtf8(right.GetAuthor()) << '|' << cru::ToUtf8(right.GetPress()) - << '|' << right.GetStockCount(); - return left; -} - -void PrettyPrint(std::wostream &stream, const Book &book) { - stream << L"ISBN: " << ToWStringView(book.GetIsbn()) << L"\n"; - stream << L"标题: " << ToWStringView(book.GetTitle()) << L"\n"; - stream << L"类型: " << ToWStringView(book.GetType()) << L"\n"; - stream << L"作者: " << ToWStringView(book.GetAuthor()) << L"\n"; - stream << L"出版社: " << ToWStringView(book.GetPress()) << L"\n"; - stream << L"库存: " << book.GetStockCount() << L"\n"; -} - -class Vendor final { -public: - Vendor() = default; - Vendor(int id, std::u16string name, std::u16string type, - std::u16string address, std::u16string phone) - : id_(id), name_(std::move(name)), type_(std::move(type)), - address_(std::move(address)), phone_(std::move(phone)) {} - - CRU_DEFAULT_COPY(Vendor) - CRU_DEFAULT_MOVE(Vendor) - - ~Vendor() = default; - -public: - int GetId() const { return id_; } - void SetId(int id) { id_ = id; } - - std::u16string GetName() const { return name_; } - void SetName(std::u16string name) { name_ = std::move(name); } - - std::u16string GetType() const { return type_; } - void SetType(std::u16string type) { type_ = std::move(type); } - - std::u16string GetAddress() const { return address_; } - void SetAddress(std::u16string address) { address_ = std::move(address); } - - std::u16string GetPhone() const { return phone_; } - void SetPhone(std::u16string phone) { phone_ = std::move(phone); } - -private: - int id_; - std::u16string name_; - std::u16string type_; - std::u16string address_; - std::u16string phone_; -}; - -std::istream &operator>>(std::istream &left, Vendor &right) { - std::string line; - std::getline(left, line); - - std::vector fields = SplitByVerticalLine(line, 5); - - right.SetId(std::stoi(std::string(fields[0]))); - right.SetName(cru::ToUtf16(fields[1])); - right.SetType(cru::ToUtf16(fields[2])); - right.SetAddress(cru::ToUtf16(fields[3])); - right.SetPhone(cru::ToUtf16(fields[4])); - - return left; -} - -std::ostream &operator<<(std::ostream &left, const Vendor &right) { - left << right.GetId() << '|' << cru::ToUtf8(right.GetName()) << '|' - << cru::ToUtf8(right.GetType()) << '|' << cru::ToUtf8(right.GetAddress()) - << '|' << cru::ToUtf8(right.GetPhone()); - return left; -} - -class Record final { -public: - Record(); - - CRU_DEFAULT_COPY(Record); - CRU_DEFAULT_MOVE(Record); - - ~Record() = default; - -public: - void WriteTo(std::ostream &stream); - void ReadFrom(std::istream &stream); - - const std::vector &GetBooks() const { return books_; } - const std::vector &GetVendors() const { return vendors_; } - - // TODO: Implementation - std::optional FindBookByIsbn(std::u16string_view isbn); - - // TODO: Implementation - void RemoveBookByIsbn(std::u16string_view isbn); - -private: - std::vector books_; - std::vector vendors_; -}; - -void Record::WriteTo(std::ostream &stream) { - stream << books_.size() << ' ' << vendors_.size() << '\n'; - for (const auto &book : books_) { - stream << book << '\n'; - } - for (const auto &vendor : vendors_) { - stream << vendor << '\n'; - } -} - -void Record::ReadFrom(std::istream &stream) { - books_.clear(); - vendors_.clear(); - int book_count, vendor_count; - stream >> book_count >> vendor_count; - stream >> std::ws; - for (int i = 0; i < book_count; i++) { - Book book; - stream >> book; - books_.push_back(std::move(book)); - } - for (int i = 0; i < vendor_count; i++) { - Vendor vendor; - stream >> vendor; - vendors_.push_back(std::move(vendor)); - } -} + QMainWindow main_window; -int main() { - Record record; + QWidget centeral_area; + QVBoxLayout central_area_layout; + centeral_area.setLayout(¢ral_area_layout); - while (true) { - std::wcout << L"1. 查询 2. 添加 0. 退出\n"; - int choice = 0; - std::wcin >> choice; - if (choice == 1) { - std::wcout - << L"1. 图书 2. 供应商\n输入数字选择操作,其他任意字符将退出程序。\n"; - choice = 0; - std::wcin >> choice; - if (choice == 1) { - std::wcout << L"请输入图书编号:\n"; - std::wstring isbn; - std::wcin >> isbn; - auto find_result = record.FindBookByIsbn(ToUtf16View(isbn)); - if (find_result) { - PrettyPrint(std::wcout, *find_result); - std::wcout << L"0. 返回主菜单 1. 修改 2. 删除\n"; - choice = 0; - std::wcin >> choice; - if (choice == 1) { - // TODO: - } else if (choice == 2) { - record.RemoveBookByIsbn(ToUtf16View(isbn)); - std::wcout << L"删除成功。\n"; - } - } else { - std::wcout << L"该书不存在。\n"; - } - } else if (choice == 2) { + main_window.setCentralWidget(¢eral_area); - } else { - return 0; - } + QWidget top_bar; + QHBoxLayout top_bar_layout; + top_bar.setLayout(&top_bar_layout); + central_area_layout.addWidget(&top_bar); - } else if (choice == 2) { + QPushButton import_button(QStringLiteral("导入")); + QPushButton export_button(QStringLiteral("导出")); + top_bar_layout.addWidget(&import_button); + top_bar_layout.addWidget(&export_button); - } else { - return 0; - } - } + main_window.show(); - return 0; + return application.exec(); } -- cgit v1.2.3 From 27de9449aac97c37850672393af45c9d3eb4813c Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 30 Dec 2020 15:37:57 +0800 Subject: import(life): ... --- works/life/cpp-practicum/main.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'works/life/cpp-practicum/main.cpp') diff --git a/works/life/cpp-practicum/main.cpp b/works/life/cpp-practicum/main.cpp index a6c92e5..9244342 100644 --- a/works/life/cpp-practicum/main.cpp +++ b/works/life/cpp-practicum/main.cpp @@ -1,5 +1,3 @@ -#include "qboxlayout.h" -#include "qnamespace.h" #include #include #include -- cgit v1.2.3 From 1e148e88abe8aca881590f4b9d992a2f03587ed7 Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 30 Dec 2020 15:53:16 +0800 Subject: import(life): ... --- works/life/cpp-practicum/main.cpp | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) (limited to 'works/life/cpp-practicum/main.cpp') diff --git a/works/life/cpp-practicum/main.cpp b/works/life/cpp-practicum/main.cpp index 9244342..14847cc 100644 --- a/works/life/cpp-practicum/main.cpp +++ b/works/life/cpp-practicum/main.cpp @@ -1,32 +1,39 @@ +#include "qboxlayout.h" +#include "qtableview.h" #include #include #include #include +#include #include #include int main(int argc, char *argv[]) { QApplication application(argc, argv); - QMainWindow main_window; + QWidget window; + QVBoxLayout window_layout; + window.setLayout(&window_layout); - QWidget centeral_area; - QVBoxLayout central_area_layout; - centeral_area.setLayout(¢ral_area_layout); - - main_window.setCentralWidget(¢eral_area); - - QWidget top_bar; - QHBoxLayout top_bar_layout; - top_bar.setLayout(&top_bar_layout); - central_area_layout.addWidget(&top_bar); + QHBoxLayout top_bar; + window_layout.addLayout(&top_bar); QPushButton import_button(QStringLiteral("导入")); QPushButton export_button(QStringLiteral("导出")); - top_bar_layout.addWidget(&import_button); - top_bar_layout.addWidget(&export_button); + top_bar.addWidget(&import_button); + top_bar.addWidget(&export_button); + top_bar.addStretch(1); + + QHBoxLayout center_area; + window_layout.addLayout(¢er_area); + + QTableView book_view; + QTableView vendor_view; + + center_area.addWidget(&book_view); + center_area.addWidget(&vendor_view); - main_window.show(); + window.show(); return application.exec(); } -- cgit v1.2.3 From d0b6d6377de44484568af2ef3a3bbd4da7cdfb4b Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 30 Dec 2020 17:23:15 +0800 Subject: import(life): ... --- works/life/cpp-practicum/Book.hpp | 2 +- works/life/cpp-practicum/Record.cpp | 147 ++++++++++++++++++++++++++++++++++++ works/life/cpp-practicum/Record.hpp | 35 ++++++--- works/life/cpp-practicum/main.cpp | 22 +++++- 4 files changed, 194 insertions(+), 12 deletions(-) (limited to 'works/life/cpp-practicum/main.cpp') diff --git a/works/life/cpp-practicum/Book.hpp b/works/life/cpp-practicum/Book.hpp index 2fb1123..250460f 100644 --- a/works/life/cpp-practicum/Book.hpp +++ b/works/life/cpp-practicum/Book.hpp @@ -44,7 +44,7 @@ private: std::u16string type_; std::u16string author_; std::u16string press_; - int stock_count_; + int stock_count_ = 0; }; QTextStream &operator>>(QTextStream &left, Book &right); diff --git a/works/life/cpp-practicum/Record.cpp b/works/life/cpp-practicum/Record.cpp index 1041b23..ac367df 100644 --- a/works/life/cpp-practicum/Record.cpp +++ b/works/life/cpp-practicum/Record.cpp @@ -1,4 +1,7 @@ #include "Record.hpp" +#include "QAbstractitemmodel.h" +#include "qnamespace.h" +#include "qvariant.h" void Record::WriteTo(QFile file) { file.open(QFile::ReadWrite | QFile::Text | QFile::Truncate); @@ -36,3 +39,147 @@ void Record::ReadFrom(QFile file) { vendors_.push_back(std::move(vendor)); } } + +int BookModel::rowCount(const QModelIndex &parent) const { + if (parent.isValid()) + return 0; + return static_cast(record_->GetBooks().size()); +} + +int BookModel::columnCount(const QModelIndex &parent) const { + if (parent.isValid()) + return 0; + return 6; +} + +QVariant BookModel::headerData(int section, Qt::Orientation orientation, + int role) const { + if (role != Qt::DisplayRole) + return QVariant(); + if (orientation == Qt::Horizontal) { + switch (section) { + case 0: + return QStringLiteral("ISBN"); + case 1: + return QStringLiteral("标题"); + case 2: + return QStringLiteral("类型"); + case 3: + return QStringLiteral("作者"); + case 4: + return QStringLiteral("出版社"); + case 5: + return QStringLiteral("库存"); + default: + return QVariant(); + } + } + return QVariant(); +} + +QVariant BookModel::data(const QModelIndex &index, int role) const { + if (role != Qt::DisplayRole) + return QVariant(); + + if (!index.isValid()) + return QVariant(); + + if (index.row() >= static_cast(record_->GetBooks().size()) || + index.row() < 0) + return QVariant(); + + int row = index.row(); + const Book &book = record_->GetBooks()[row]; + + int col = index.column(); + switch (col) { + case 0: + return QString::fromStdU16String(book.GetIsbn()); + case 1: + return QString::fromStdU16String(book.GetTitle()); + case 2: + return QString::fromStdU16String(book.GetType()); + case 3: + return QString::fromStdU16String(book.GetAuthor()); + case 4: + return QString::fromStdU16String(book.GetPress()); + case 5: + return book.GetStockCount(); + default: + return QVariant(); + } +} + +bool BookModel::setData(const QModelIndex &index, const QVariant &value, + int role) { + if (index.isValid() && role == Qt::EditRole) { + int row = index.row(); + Book &book = record_->GetBooks()[row]; + + int col = index.column(); + switch (col) { + case 0: + if (!value.canConvert()) + return false; + book.SetIsbn(value.toString().toStdU16String()); + return true; + case 1: + if (!value.canConvert()) + return false; + book.SetTitle(value.toString().toStdU16String()); + emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); + return true; + case 2: + if (!value.canConvert()) + return false; + book.SetType(value.toString().toStdU16String()); + emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); + return true; + case 3: + if (!value.canConvert()) + return false; + book.SetAuthor(value.toString().toStdU16String()); + emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); + return true; + case 4: + if (!value.canConvert()) + return false; + book.SetPress(value.toString().toStdU16String()); + emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); + return true; + case 5: + if (!value.canConvert()) + return false; + book.SetStockCount(value.toInt()); + emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); + return true; + default: + return false; + } + } + return false; +} + +Qt::ItemFlags BookModel::flags(const QModelIndex &index) const { + if (!index.isValid()) + return Qt::ItemIsEnabled; + + return QAbstractTableModel::flags(index) | Qt::ItemIsEditable; +} + +bool BookModel::insertRows(int row, int count, const QModelIndex &parent) { + beginInsertRows(parent, row, row + count - 1); + for (int i = 0; i < count; i++) { + record_->GetBooks().insert(record_->GetBooks().cbegin() + row, Book()); + } + endInsertRows(); + return true; +} + +bool BookModel::removeRows(int row, int count, const QModelIndex &parent) { + beginRemoveRows(parent, row, row + count - 1); + record_->GetBooks().erase(record_->GetBooks().cbegin() + row, + record_->GetBooks().cbegin() + row + count); + endRemoveRows(); + return true; +} diff --git a/works/life/cpp-practicum/Record.hpp b/works/life/cpp-practicum/Record.hpp index e39e626..78b70ba 100644 --- a/works/life/cpp-practicum/Record.hpp +++ b/works/life/cpp-practicum/Record.hpp @@ -4,13 +4,14 @@ #include "Book.hpp" #include "Vendor.hpp" +#include #include #include #include class Record final { public: - Record(); + Record() = default; CRU_DEFAULT_COPY(Record); CRU_DEFAULT_MOVE(Record); @@ -21,16 +22,32 @@ public: void WriteTo(QFile file); void ReadFrom(QFile file); - const std::vector &GetBooks() const { return books_; } - const std::vector &GetVendors() const { return vendors_; } - - // TODO: Implementation - std::optional FindBookByIsbn(std::u16string_view isbn); - - // TODO: Implementation - void RemoveBookByIsbn(std::u16string_view isbn); + std::vector &GetBooks() { return books_; } + std::vector &GetVendors() { return vendors_; } private: std::vector books_; std::vector vendors_; }; + +class BookModel : public QAbstractTableModel { +public: + explicit BookModel(Record *record) : record_(record) {} + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const override; + QVariant data(const QModelIndex &index, + int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole) override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + bool insertRows(int row, int count, + const QModelIndex &parent = QModelIndex()) override; + bool removeRows(int row, int count, + const QModelIndex &parent = QModelIndex()) override; + +private: + Record *record_; +}; \ No newline at end of file diff --git a/works/life/cpp-practicum/main.cpp b/works/life/cpp-practicum/main.cpp index 14847cc..b7e5db5 100644 --- a/works/life/cpp-practicum/main.cpp +++ b/works/life/cpp-practicum/main.cpp @@ -1,7 +1,9 @@ -#include "qboxlayout.h" -#include "qtableview.h" +#include "Record.hpp" +#include "qabstractitemview.h" + #include #include +#include #include #include #include @@ -20,19 +22,35 @@ int main(int argc, char *argv[]) { QPushButton import_button(QStringLiteral("导入")); QPushButton export_button(QStringLiteral("导出")); + QPushButton add_book_button(QStringLiteral("添加书")); + top_bar.addWidget(&import_button); top_bar.addWidget(&export_button); top_bar.addStretch(1); + top_bar.addWidget(&add_book_button); QHBoxLayout center_area; window_layout.addLayout(¢er_area); QTableView book_view; + book_view.verticalHeader()->hide(); + book_view.setSelectionBehavior(QAbstractItemView::SelectRows); + book_view.setEditTriggers(QAbstractItemView::DoubleClicked); + book_view.setSelectionMode(QAbstractItemView::SingleSelection); QTableView vendor_view; center_area.addWidget(&book_view); center_area.addWidget(&vendor_view); + Record record; + BookModel book_model(&record); + + book_view.setModel(&book_model); + + QObject::connect(&add_book_button, &QPushButton::clicked, [&book_model]() { + book_model.insertRow(book_model.rowCount()); + }); + window.show(); return application.exec(); -- cgit v1.2.3 From 5dc5500d0a31497f69404c463f39e44f8a70f3df Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 30 Dec 2020 17:27:48 +0800 Subject: import(life): ... --- works/life/cpp-practicum/Record.cpp | 3 --- works/life/cpp-practicum/main.cpp | 1 - 2 files changed, 4 deletions(-) (limited to 'works/life/cpp-practicum/main.cpp') diff --git a/works/life/cpp-practicum/Record.cpp b/works/life/cpp-practicum/Record.cpp index ac367df..4d7d4a9 100644 --- a/works/life/cpp-practicum/Record.cpp +++ b/works/life/cpp-practicum/Record.cpp @@ -1,7 +1,4 @@ #include "Record.hpp" -#include "QAbstractitemmodel.h" -#include "qnamespace.h" -#include "qvariant.h" void Record::WriteTo(QFile file) { file.open(QFile::ReadWrite | QFile::Text | QFile::Truncate); diff --git a/works/life/cpp-practicum/main.cpp b/works/life/cpp-practicum/main.cpp index b7e5db5..9ecc308 100644 --- a/works/life/cpp-practicum/main.cpp +++ b/works/life/cpp-practicum/main.cpp @@ -1,5 +1,4 @@ #include "Record.hpp" -#include "qabstractitemview.h" #include #include -- cgit v1.2.3 From 6469683b4cca6aaae9d27e34c17b8f75a988fa81 Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 30 Dec 2020 20:04:07 +0800 Subject: import(life): ... --- works/life/cpp-practicum/Record.cpp | 179 ++++++++++++++++++++++++++++++++++-- works/life/cpp-practicum/Record.hpp | 31 ++++++- works/life/cpp-practicum/main.cpp | 98 ++++++++++++++++---- 3 files changed, 279 insertions(+), 29 deletions(-) (limited to 'works/life/cpp-practicum/main.cpp') diff --git a/works/life/cpp-practicum/Record.cpp b/works/life/cpp-practicum/Record.cpp index 4d7d4a9..bdee28b 100644 --- a/works/life/cpp-practicum/Record.cpp +++ b/works/life/cpp-practicum/Record.cpp @@ -1,10 +1,8 @@ #include "Record.hpp" -void Record::WriteTo(QFile file) { - file.open(QFile::ReadWrite | QFile::Text | QFile::Truncate); - QTextStream stream(&file); - stream.setCodec("UTF-8"); +#include +void Record::WriteTo(QTextStream &stream) { stream << books_.size() << ' ' << vendors_.size() << '\n'; for (const auto &book : books_) { stream << book << '\n'; @@ -14,11 +12,7 @@ void Record::WriteTo(QFile file) { } } -void Record::ReadFrom(QFile file) { - file.open(QFile::ReadOnly | QFile::Text); - QTextStream stream(&file); - stream.setCodec("UTF-8"); - +void Record::ReadFrom(QTextStream &stream) { books_.clear(); vendors_.clear(); @@ -180,3 +174,170 @@ bool BookModel::removeRows(int row, int count, const QModelIndex &parent) { endRemoveRows(); return true; } + +void BookModel::sort(int column, Qt::SortOrder order) { + if (column == 0) { + beginResetModel(); + if (order == Qt::AscendingOrder) { + std::sort(record_->GetBooks().begin(), record_->GetBooks().end(), + [](const Book &left, const Book &right) { + return left.GetIsbn() < right.GetIsbn(); + }); + } else { + std::sort(record_->GetBooks().begin(), record_->GetBooks().end(), + [](const Book &left, const Book &right) { + return left.GetIsbn() > right.GetIsbn(); + }); + } + endResetModel(); + } else if (column == 1) { + beginResetModel(); + if (order == Qt::AscendingOrder) { + std::sort(record_->GetBooks().begin(), record_->GetBooks().end(), + [](const Book &left, const Book &right) { + return left.GetTitle() < right.GetTitle(); + }); + } else { + std::sort(record_->GetBooks().begin(), record_->GetBooks().end(), + [](const Book &left, const Book &right) { + return left.GetTitle() > right.GetTitle(); + }); + } + endResetModel(); + } +} + +int VendorModel::rowCount(const QModelIndex &parent) const { + if (parent.isValid()) + return 0; + return static_cast(record_->GetVendors().size()); +} + +int VendorModel::columnCount(const QModelIndex &parent) const { + if (parent.isValid()) + return 0; + return 5; +} + +QVariant VendorModel::headerData(int section, Qt::Orientation orientation, + int role) const { + if (role != Qt::DisplayRole) + return QVariant(); + if (orientation == Qt::Horizontal) { + switch (section) { + case 0: + return QStringLiteral("编号"); + case 1: + return QStringLiteral("名称"); + case 2: + return QStringLiteral("类型"); + case 3: + return QStringLiteral("地址"); + case 4: + return QStringLiteral("电话"); + default: + return QVariant(); + } + } + return QVariant(); +} + +QVariant VendorModel::data(const QModelIndex &index, int role) const { + if (role != Qt::DisplayRole) + return QVariant(); + + if (!index.isValid()) + return QVariant(); + + if (index.row() >= static_cast(record_->GetVendors().size()) || + index.row() < 0) + return QVariant(); + + int row = index.row(); + const Vendor &vendor = record_->GetVendors()[row]; + + int col = index.column(); + switch (col) { + case 0: + return vendor.GetId(); + case 1: + return QString::fromStdU16String(vendor.GetName()); + case 2: + return QString::fromStdU16String(vendor.GetType()); + case 3: + return QString::fromStdU16String(vendor.GetAddress()); + case 4: + return QString::fromStdU16String(vendor.GetPhone()); + default: + return QVariant(); + } +} + +bool VendorModel::setData(const QModelIndex &index, const QVariant &value, + int role) { + if (index.isValid() && role == Qt::EditRole) { + int row = index.row(); + Vendor &vendor = record_->GetVendors()[row]; + + int col = index.column(); + switch (col) { + case 0: + if (!value.canConvert()) + return false; + vendor.SetId(value.toInt()); + return true; + case 1: + if (!value.canConvert()) + return false; + vendor.SetName(value.toString().toStdU16String()); + emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); + return true; + case 2: + if (!value.canConvert()) + return false; + vendor.SetType(value.toString().toStdU16String()); + emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); + return true; + case 3: + if (!value.canConvert()) + return false; + vendor.SetAddress(value.toString().toStdU16String()); + emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); + return true; + case 4: + if (!value.canConvert()) + return false; + vendor.SetPhone(value.toString().toStdU16String()); + emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); + return true; + default: + return false; + } + } + return false; +} + +Qt::ItemFlags VendorModel::flags(const QModelIndex &index) const { + if (!index.isValid()) + return Qt::ItemIsEnabled; + + return QAbstractTableModel::flags(index) | Qt::ItemIsEditable; +} + +bool VendorModel::insertRows(int row, int count, const QModelIndex &parent) { + beginInsertRows(parent, row, row + count - 1); + for (int i = 0; i < count; i++) { + record_->GetVendors().insert(record_->GetVendors().cbegin() + row, + Vendor()); + } + endInsertRows(); + return true; +} + +bool VendorModel::removeRows(int row, int count, const QModelIndex &parent) { + beginRemoveRows(parent, row, row + count - 1); + record_->GetVendors().erase(record_->GetVendors().cbegin() + row, + record_->GetVendors().cbegin() + row + count); + endRemoveRows(); + return true; +} diff --git a/works/life/cpp-practicum/Record.hpp b/works/life/cpp-practicum/Record.hpp index 78b70ba..c379f04 100644 --- a/works/life/cpp-practicum/Record.hpp +++ b/works/life/cpp-practicum/Record.hpp @@ -5,7 +5,7 @@ #include "Vendor.hpp" #include -#include +#include #include #include @@ -19,8 +19,8 @@ public: ~Record() = default; public: - void WriteTo(QFile file); - void ReadFrom(QFile file); + void WriteTo(QTextStream &stream); + void ReadFrom(QTextStream &stream); std::vector &GetBooks() { return books_; } std::vector &GetVendors() { return vendors_; } @@ -47,7 +47,30 @@ public: const QModelIndex &parent = QModelIndex()) override; bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; private: Record *record_; -}; \ No newline at end of file +}; + +class VendorModel : public QAbstractTableModel { +public: + explicit VendorModel(Record *record) : record_(record) {} + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const override; + QVariant data(const QModelIndex &index, + int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole) override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + bool insertRows(int row, int count, + const QModelIndex &parent = QModelIndex()) override; + bool removeRows(int row, int count, + const QModelIndex &parent = QModelIndex()) override; + +private: + Record *record_; +}; diff --git a/works/life/cpp-practicum/main.cpp b/works/life/cpp-practicum/main.cpp index 9ecc308..399c82c 100644 --- a/works/life/cpp-practicum/main.cpp +++ b/works/life/cpp-practicum/main.cpp @@ -1,6 +1,8 @@ #include "Record.hpp" #include +#include +#include #include #include #include @@ -8,49 +10,113 @@ #include #include #include +#include int main(int argc, char *argv[]) { QApplication application(argc, argv); + Record record; + + QDir app_dir(application.applicationDirPath()); + QFile data_file = app_dir.filePath("data.txt"); + std::unique_ptr stream; + if (data_file.exists()) { + data_file.open(QFile::ReadWrite); + stream.reset(new QTextStream(&data_file)); + stream->setCodec("UTF-8"); + try { + record.ReadFrom(*stream); + } catch (...) { + } + } else { + data_file.open(QFile::ReadWrite); + stream.reset(new QTextStream(&data_file)); + stream->setCodec("UTF-8"); + } + QWidget window; QVBoxLayout window_layout; window.setLayout(&window_layout); - QHBoxLayout top_bar; - window_layout.addLayout(&top_bar); + QHBoxLayout center_area; + window_layout.addLayout(¢er_area); - QPushButton import_button(QStringLiteral("导入")); - QPushButton export_button(QStringLiteral("导出")); - QPushButton add_book_button(QStringLiteral("添加书")); + QVBoxLayout book_area; + QVBoxLayout vendor_area; + center_area.addLayout(&book_area); + center_area.addLayout(&vendor_area); - top_bar.addWidget(&import_button); - top_bar.addWidget(&export_button); - top_bar.addStretch(1); - top_bar.addWidget(&add_book_button); + QHBoxLayout book_top_area; + QHBoxLayout vendor_top_area; + book_area.addLayout(&book_top_area); + vendor_area.addLayout(&vendor_top_area); - QHBoxLayout center_area; - window_layout.addLayout(¢er_area); + QPushButton book_add_button(QStringLiteral("添加")); + QPushButton book_remove_button(QStringLiteral("删除")); + + book_top_area.addStretch(1); + book_top_area.addWidget(&book_add_button); + book_top_area.addWidget(&book_remove_button); + + QPushButton vendor_add_button(QStringLiteral("添加")); + QPushButton vendor_remove_button(QStringLiteral("删除")); + + vendor_top_area.addStretch(1); + vendor_top_area.addWidget(&vendor_add_button); + vendor_top_area.addWidget(&vendor_remove_button); QTableView book_view; book_view.verticalHeader()->hide(); book_view.setSelectionBehavior(QAbstractItemView::SelectRows); book_view.setEditTriggers(QAbstractItemView::DoubleClicked); book_view.setSelectionMode(QAbstractItemView::SingleSelection); + book_view.setSortingEnabled(true); + QTableView vendor_view; + vendor_view.verticalHeader()->hide(); + vendor_view.setSelectionBehavior(QAbstractItemView::SelectRows); + vendor_view.setEditTriggers(QAbstractItemView::DoubleClicked); + vendor_view.setSelectionMode(QAbstractItemView::SingleSelection); - center_area.addWidget(&book_view); - center_area.addWidget(&vendor_view); + book_area.addWidget(&book_view, 1); + vendor_area.addWidget(&vendor_view, 1); - Record record; BookModel book_model(&record); + VendorModel vendor_model(&record); book_view.setModel(&book_model); + vendor_view.setModel(&vendor_model); - QObject::connect(&add_book_button, &QPushButton::clicked, [&book_model]() { + QObject::connect(&book_add_button, &QPushButton::clicked, [&book_model]() { book_model.insertRow(book_model.rowCount()); }); + QObject::connect( + &book_remove_button, &QPushButton::clicked, [&book_view, &book_model]() { + auto selected_rows = book_view.selectionModel()->selectedRows(); + for (const auto &row : selected_rows) { + book_model.removeRow(row.row()); + } + }); + + QObject::connect( + &vendor_add_button, &QPushButton::clicked, + [&vendor_model]() { vendor_model.insertRow(vendor_model.rowCount()); }); + + QObject::connect(&vendor_remove_button, &QPushButton::clicked, + [&vendor_view, &vendor_model]() { + auto selected_rows = + vendor_view.selectionModel()->selectedRows(); + for (const auto &row : selected_rows) { + vendor_model.removeRow(row.row()); + } + }); + window.show(); - return application.exec(); + int result = application.exec(); + + record.WriteTo(*stream); + + return result; } -- cgit v1.2.3