diff options
Diffstat (limited to 'works/life/cpp-practicum')
| -rw-r--r-- | works/life/cpp-practicum/.gitignore | 597 | ||||
| -rw-r--r-- | works/life/cpp-practicum/Base.hpp | 25 | ||||
| -rw-r--r-- | works/life/cpp-practicum/Book.cpp | 36 | ||||
| -rw-r--r-- | works/life/cpp-practicum/Book.hpp | 51 | ||||
| -rw-r--r-- | works/life/cpp-practicum/CMakeLists.txt | 33 | ||||
| -rw-r--r-- | works/life/cpp-practicum/Record.cpp | 343 | ||||
| -rw-r--r-- | works/life/cpp-practicum/Record.hpp | 76 | ||||
| -rw-r--r-- | works/life/cpp-practicum/Vendor.cpp | 33 | ||||
| -rw-r--r-- | works/life/cpp-practicum/Vendor.hpp | 45 | ||||
| -rw-r--r-- | works/life/cpp-practicum/main.cpp | 122 | ||||
| -rw-r--r-- | works/life/cpp-practicum/vcpkg.json | 6 | 
11 files changed, 1367 insertions, 0 deletions
diff --git a/works/life/cpp-practicum/.gitignore b/works/life/cpp-practicum/.gitignore new file mode 100644 index 0000000..42ea155 --- /dev/null +++ b/works/life/cpp-practicum/.gitignore @@ -0,0 +1,597 @@ +
 +# Created by https://www.gitignore.io/api/c++,cmake,python,visualstudio,visualstudiocode
 +# Edit at https://www.gitignore.io/?templates=c++,cmake,python,visualstudio,visualstudiocode
 +
 +### C++ ###
 +# Prerequisites
 +*.d
 +
 +# Compiled Object files
 +*.slo
 +*.lo
 +*.o
 +*.obj
 +
 +# Precompiled Headers
 +*.gch
 +*.pch
 +
 +# Compiled Dynamic libraries
 +*.so
 +*.dylib
 +*.dll
 +
 +# Fortran module files
 +*.mod
 +*.smod
 +
 +# Compiled Static libraries
 +*.lai
 +*.la
 +*.a
 +*.lib
 +
 +# Executables
 +*.exe
 +*.out
 +*.app
 +
 +### CMake ###
 +CMakeLists.txt.user
 +CMakeCache.txt
 +CMakeFiles
 +CMakeScripts
 +Testing
 +Makefile
 +cmake_install.cmake
 +install_manifest.txt
 +compile_commands.json
 +CTestTestfile.cmake
 +_deps
 +
 +### CMake Patch ###
 +# External projects
 +*-prefix/
 +
 +### Python ###
 +# Byte-compiled / optimized / DLL files
 +__pycache__/
 +*.py[cod]
 +*$py.class
 +
 +# C extensions
 +
 +# Distribution / packaging
 +.Python
 +build/
 +develop-eggs/
 +dist/
 +downloads/
 +eggs/
 +.eggs/
 +lib/
 +lib64/
 +parts/
 +sdist/
 +var/
 +wheels/
 +pip-wheel-metadata/
 +share/python-wheels/
 +*.egg-info/
 +.installed.cfg
 +*.egg
 +MANIFEST
 +
 +# PyInstaller
 +#  Usually these files are written by a python script from a template
 +#  before PyInstaller builds the exe, so as to inject date/other infos into it.
 +*.manifest
 +*.spec
 +
 +# Installer logs
 +pip-log.txt
 +pip-delete-this-directory.txt
 +
 +# Unit test / coverage reports
 +htmlcov/
 +.tox/
 +.nox/
 +.coverage
 +.coverage.*
 +.cache
 +nosetests.xml
 +coverage.xml
 +*.cover
 +.hypothesis/
 +.pytest_cache/
 +
 +# Translations
 +*.mo
 +*.pot
 +
 +# Django stuff:
 +*.log
 +local_settings.py
 +db.sqlite3
 +
 +# Flask stuff:
 +instance/
 +.webassets-cache
 +
 +# Scrapy stuff:
 +.scrapy
 +
 +# Sphinx documentation
 +docs/_build/
 +
 +# PyBuilder
 +target/
 +
 +# Jupyter Notebook
 +.ipynb_checkpoints
 +
 +# IPython
 +profile_default/
 +ipython_config.py
 +
 +# pyenv
 +.python-version
 +
 +# pipenv
 +#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
 +#   However, in case of collaboration, if having platform-specific dependencies or dependencies
 +#   having no cross-platform support, pipenv may install dependencies that don’t work, or not
 +#   install all needed dependencies.
 +#Pipfile.lock
 +
 +# celery beat schedule file
 +celerybeat-schedule
 +
 +# SageMath parsed files
 +*.sage.py
 +
 +# Environments
 +.env
 +.venv
 +env/
 +venv/
 +ENV/
 +env.bak/
 +venv.bak/
 +
 +# Spyder project settings
 +.spyderproject
 +.spyproject
 +
 +# Rope project settings
 +.ropeproject
 +
 +# mkdocs documentation
 +/site
 +
 +# mypy
 +.mypy_cache/
 +.dmypy.json
 +dmypy.json
 +
 +# Pyre type checker
 +.pyre/
 +
 +### VisualStudioCode ###
 +.vscode/*
 +!.vscode/c_cpp_properties.json
 +!.vscode/settings.json
 +!.vscode/tasks.json
 +!.vscode/launch.json
 +!.vscode/extensions.json
 +
 +### VisualStudioCode Patch ###
 +# Ignore all local history of files
 +.history
 +
 +### VisualStudio ###
 +## 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/
 +[Aa][Rr][Mm]/
 +[Aa][Rr][Mm]64/
 +bld/
 +[Bb]in/
 +[Oo]bj/
 +[Ll]og/
 +
 +# 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
 +
 +# Build Results of an ATL Project
 +[Dd]ebugPS/
 +[Rr]eleasePS/
 +dlldata.c
 +
 +# Benchmark Results
 +BenchmarkDotNet.Artifacts/
 +
 +# .NET Core
 +project.lock.json
 +project.fragment.lock.json
 +artifacts/
 +
 +# StyleCop
 +StyleCopReport.xml
 +
 +# Files built by Visual Studio
 +*_i.c
 +*_p.c
 +*_h.h
 +*.ilk
 +*.meta
 +*.iobj
 +*.pdb
 +*.ipdb
 +*.pgc
 +*.pgd
 +*.rsp
 +*.sbr
 +*.tlb
 +*.tli
 +*.tlh
 +*.tmp
 +*.tmp_proj
 +*_wpftmp.csproj
 +*.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
 +
 +# JustCode is a .NET coding add-in
 +.JustCode
 +
 +# TeamCity is a build add-in
 +_TeamCity*
 +
 +# DotCover is a Code Coverage Tool
 +*.dotCover
 +
 +# AxoCover is a Code Coverage Tool
 +.axoCover/*
 +!.axoCover/settings.json
 +
 +# 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
 +# 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
 +*- Backup*.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)
 +*.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/
 +
 +# End of https://www.gitignore.io/api/c++,cmake,python,visualstudio,visualstudiocode
 +
 +# Created by https://www.gitignore.io/api/emacs
 +# Edit at https://www.gitignore.io/?templates=emacs
 +
 +### Emacs ###
 +# -*- mode: gitignore; -*-
 +*~
 +\#*\#
 +/.emacs.desktop
 +/.emacs.desktop.lock
 +*.elc
 +auto-save-list
 +tramp
 +.\#*
 +
 +# Org-mode
 +.org-id-locations
 +*_archive
 +
 +# flymake-mode
 +*_flymake.*
 +
 +# eshell files
 +/eshell/history
 +/eshell/lastdir
 +
 +# elpa packages
 +/elpa/
 +
 +# reftex files
 +*.rel
 +
 +# AUCTeX auto folder
 +/auto/
 +
 +# cask packages
 +.cask/
 +dist/
 +
 +# Flycheck
 +flycheck_*.el
 +
 +# server auth directory
 +/server/
 +
 +# projectiles files
 +.projectile
 +
 +# directory configuration
 +.dir-locals.el
 +
 +# network security
 +/network-security.data
 +
 +
 +# End of https://www.gitignore.io/api/emacs
 +
 +compile_flags.txt
 +.clangd
 +.kdev4
 +CruUI.kdev4
 +
 +vcpkg_installed
 diff --git a/works/life/cpp-practicum/Base.hpp b/works/life/cpp-practicum/Base.hpp new file mode 100644 index 0000000..44a9d6c --- /dev/null +++ b/works/life/cpp-practicum/Base.hpp @@ -0,0 +1,25 @@ +#pragma once
 +
 +#include <exception>
 +#include <stdexcept>
 +
 +#define CRU_DEFAULT_COPY(classname)                                            \
 +  classname(const classname &) = default;                                      \
 +  classname &operator=(const classname &) = default;
 +
 +#define CRU_DEFAULT_MOVE(classname)                                            \
 +  classname(classname &&) = default;                                           \
 +  classname &operator=(classname &&) = default;
 +
 +#define CRU_DELETE_COPY(classname)                                             \
 +  classname(const classname &) = delete;                                       \
 +  classname &operator=(const classname &) = delete;
 +
 +#define CRU_DELETE_MOVE(classname)                                             \
 +  classname(classname &&) = delete;                                            \
 +  classname &operator=(classname &&) = delete;
 +
 +class SerializationException : public std::runtime_error {
 +public:
 +  using runtime_error::runtime_error;
 +};
 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 <QString>
 +
 +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..250460f --- /dev/null +++ b/works/life/cpp-practicum/Book.hpp @@ -0,0 +1,51 @@ +#pragma once
 +#include "Base.hpp"
 +
 +#include <QTextStream>
 +#include <string>
 +
 +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_ = 0;
 +};
 +
 +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 new file mode 100644 index 0000000..346fdb4 --- /dev/null +++ b/works/life/cpp-practicum/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.14)
 +
 +set(CMAKE_TOOLCHAIN_FILE $ENV{VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake
 +  CACHE STRING "Vcpkg toolchain file")
 +
 +project(CppPraticum)
 +
 +enable_testing()
 +
 +set(CMAKE_CXX_STANDARD 17)
 +
 +if (MSVC)
 +	string(REGEX REPLACE "/W[0-4]\\s*" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
 +	add_compile_options(/utf-8 /W4 /WX)
 +endif()
 +
 +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..bdee28b --- /dev/null +++ b/works/life/cpp-practicum/Record.cpp @@ -0,0 +1,343 @@ +#include "Record.hpp"
 +
 +#include <algorithm>
 +
 +void Record::WriteTo(QTextStream &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(QTextStream &stream) {
 +  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));
 +  }
 +}
 +
 +int BookModel::rowCount(const QModelIndex &parent) const {
 +  if (parent.isValid())
 +    return 0;
 +  return static_cast<int>(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<int>(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<QString>())
 +        return false;
 +      book.SetIsbn(value.toString().toStdU16String());
 +      return true;
 +    case 1:
 +      if (!value.canConvert<QString>())
 +        return false;
 +      book.SetTitle(value.toString().toStdU16String());
 +      emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
 +      return true;
 +    case 2:
 +      if (!value.canConvert<QString>())
 +        return false;
 +      book.SetType(value.toString().toStdU16String());
 +      emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
 +      return true;
 +    case 3:
 +      if (!value.canConvert<QString>())
 +        return false;
 +      book.SetAuthor(value.toString().toStdU16String());
 +      emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
 +      return true;
 +    case 4:
 +      if (!value.canConvert<QString>())
 +        return false;
 +      book.SetPress(value.toString().toStdU16String());
 +      emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
 +      return true;
 +    case 5:
 +      if (!value.canConvert<int>())
 +        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;
 +}
 +
 +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<int>(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<int>(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<int>())
 +        return false;
 +      vendor.SetId(value.toInt());
 +      return true;
 +    case 1:
 +      if (!value.canConvert<QString>())
 +        return false;
 +      vendor.SetName(value.toString().toStdU16String());
 +      emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
 +      return true;
 +    case 2:
 +      if (!value.canConvert<QString>())
 +        return false;
 +      vendor.SetType(value.toString().toStdU16String());
 +      emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
 +      return true;
 +    case 3:
 +      if (!value.canConvert<QString>())
 +        return false;
 +      vendor.SetAddress(value.toString().toStdU16String());
 +      emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
 +      return true;
 +    case 4:
 +      if (!value.canConvert<QString>())
 +        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 new file mode 100644 index 0000000..c379f04 --- /dev/null +++ b/works/life/cpp-practicum/Record.hpp @@ -0,0 +1,76 @@ +#pragma once
 +#include "Base.hpp"
 +
 +#include "Book.hpp"
 +#include "Vendor.hpp"
 +
 +#include <QAbstractTableModel>
 +#include <QTextStream>
 +#include <optional>
 +#include <vector>
 +
 +class Record final {
 +public:
 +  Record() = default;
 +
 +  CRU_DEFAULT_COPY(Record);
 +  CRU_DEFAULT_MOVE(Record);
 +
 +  ~Record() = default;
 +
 +public:
 +  void WriteTo(QTextStream &stream);
 +  void ReadFrom(QTextStream &stream);
 +
 +  std::vector<Book> &GetBooks() { return books_; }
 +  std::vector<Vendor> &GetVendors() { return vendors_; }
 +
 +private:
 +  std::vector<Book> books_;
 +  std::vector<Vendor> 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;
 +  void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
 +
 +private:
 +  Record *record_;
 +};
 +
 +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/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 <QString>
 +
 +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 <QTextStream>
 +#include <string>
 +
 +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 new file mode 100644 index 0000000..399c82c --- /dev/null +++ b/works/life/cpp-practicum/main.cpp @@ -0,0 +1,122 @@ +#include "Record.hpp"
 +
 +#include <QApplication>
 +#include <QDir>
 +#include <QFile>
 +#include <QHBoxLayout>
 +#include <QHeaderView>
 +#include <QMainWindow>
 +#include <QPushButton>
 +#include <QTableView>
 +#include <QVBoxLayout>
 +#include <QWidget>
 +#include <memory>
 +
 +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<QTextStream> 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 center_area;
 +  window_layout.addLayout(¢er_area);
 +
 +  QVBoxLayout book_area;
 +  QVBoxLayout vendor_area;
 +  center_area.addLayout(&book_area);
 +  center_area.addLayout(&vendor_area);
 +
 +  QHBoxLayout book_top_area;
 +  QHBoxLayout vendor_top_area;
 +  book_area.addLayout(&book_top_area);
 +  vendor_area.addLayout(&vendor_top_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);
 +
 +  book_area.addWidget(&book_view, 1);
 +  vendor_area.addWidget(&vendor_view, 1);
 +
 +  BookModel book_model(&record);
 +  VendorModel vendor_model(&record);
 +
 +  book_view.setModel(&book_model);
 +  vendor_view.setModel(&vendor_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();
 +
 +  int result = application.exec();
 +
 +  record.WriteTo(*stream);
 +
 +  return result;
 +}
 diff --git a/works/life/cpp-practicum/vcpkg.json b/works/life/cpp-practicum/vcpkg.json new file mode 100644 index 0000000..30e99f4 --- /dev/null +++ b/works/life/cpp-practicum/vcpkg.json @@ -0,0 +1,6 @@ +{
 +  "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
 +  "name": "cpp-praticum",
 +  "version": "0.0.1",
 +  "dependencies": ["ms-gsl", "qt5"]
 +}
  | 
