From 605c9c9d0daa6ebdd870a7d8332d2998290858cc Mon Sep 17 00:00:00 2001 From: Wallbreaker5th <3277946283@qq.com> Date: Sat, 11 Sep 2021 14:36:02 +0800 Subject: [PATCH 1/2] Support to export a traditional task as UOJ format --- cmake/lemon-ui.cmake | 3 + src/exportingasuojformat.cpp | 192 +++++++++++++++++++ src/exportingasuojformat.h | 32 ++++ src/forms/exportingasuojformat.ui | 303 ++++++++++++++++++++++++++++++ src/summarytree.cpp | 31 +++ src/summarytree.h | 2 + translations/en_US.ts | 71 +++++++ translations/zh_CN.ts | 67 +++++++ 8 files changed, 701 insertions(+) create mode 100644 src/exportingasuojformat.cpp create mode 100644 src/exportingasuojformat.h create mode 100644 src/forms/exportingasuojformat.ui diff --git a/cmake/lemon-ui.cmake b/cmake/lemon-ui.cmake index 102f5dd4..02e56130 100644 --- a/cmake/lemon-ui.cmake +++ b/cmake/lemon-ui.cmake @@ -36,6 +36,7 @@ set(LEMON_UI_SOURCES ${LEMON_BASEDIR_UI}/editvariabledialog.cpp ${LEMON_BASEDIR_UI}/addcompilerwizard.cpp ${LEMON_BASEDIR_UI}/statisticsbrowser.cpp + ${LEMON_BASEDIR_UI}/exportingasuojformat.cpp ${LEMON_BASEDIR_UI}/component/exportutil/exportutil.cpp ) @@ -70,6 +71,7 @@ set(LEMON_UI_HEADERS ${LEMON_BASEDIR_UI}/editvariabledialog.h ${LEMON_BASEDIR_UI}/addcompilerwizard.h ${LEMON_BASEDIR_UI}/statisticsbrowser.h + ${LEMON_BASEDIR_UI}/exportingasuojformat.h ${LEMON_BASEDIR_UI}/component/exportutil/exportutil.h ) @@ -100,4 +102,5 @@ set(LEMON_UI_FORMS ${LEMON_BASEDIR_UI}/forms/editvariabledialog.ui ${LEMON_BASEDIR_UI}/forms/addcompilerwizard.ui ${LEMON_BASEDIR_UI}/forms/statisticsbrowser.ui + ${LEMON_BASEDIR_UI}/forms/exportingasuojformat.ui ) diff --git a/src/exportingasuojformat.cpp b/src/exportingasuojformat.cpp new file mode 100644 index 00000000..1fae0425 --- /dev/null +++ b/src/exportingasuojformat.cpp @@ -0,0 +1,192 @@ +#include "exportingasuojformat.h" +#include "base/settings.h" +#include "core/task.h" +#include "core/testcase.h" +#include "ui_exportingasuojformat.h" +#include + +ExportingAsUojFormat::ExportingAsUojFormat(QWidget *parent) + : QDialog(parent), ui(new Ui::ExportingAsUojFormat) { + ui->setupUi(this); + + class QValidator *timeLimitValidator = new QDoubleValidator(0, 86400, 2, this); + ui->timeLimit->setValidator(timeLimitValidator); + class QValidator *memoryLimitValidator = new QIntValidator(0, 4096, this); + ui->memoryLimit->setValidator(memoryLimitValidator); + + connect(ui->specialJudge, &QCheckBox::stateChanged, this, &ExportingAsUojFormat::specialJudgeChanged); + + ui->checker->refreshFileList(); +} + +ExportingAsUojFormat::~ExportingAsUojFormat() { delete ui; } + +void ExportingAsUojFormat::init(Task *task) { + this->task = task; + + ui->problemTitle->setText(task->getProblemTitle()); + + QList inputFileName = task->getInputFileName().split("."); + if (inputFileName.length() == 2) { + ui->inputFilePrefix->setText(inputFileName[0]); + ui->inputFileSuffix->setText(inputFileName[1]); + } + + QList outputFileName = task->getOutputFileName().split("."); + if (outputFileName.length() == 2) { + ui->outputFilePrefix->setText(outputFileName[0]); + ui->outputFileSuffix->setText(outputFileName[1]); + } + + int timeLimit = 0, memoryLimit = 0; + for (auto testcase : task->getTestCaseList()) { + timeLimit = std::max(timeLimit, testcase->getTimeLimit()); + memoryLimit = std::max(memoryLimit, testcase->getMemoryLimit()); + } + + if (timeLimit % 1000 == 0) { + ui->timeLimit->setText(QString::number(timeLimit / 1000)); + } else { + ui->timeLimit->setText(QString::number(timeLimit / 1000., 'f', 2)); + } + ui->memoryLimit->setText(QString::number(memoryLimit)); + + switch (task->getComparisonMode()) { + case Task::LineByLineMode: + ui->checker->setText("bcmp"); + break; + case Task::IgnoreSpacesMode: + ui->checker->setText("wcmp"); + break; + case Task::RealNumberMode: + ui->checker->setText(QString("rcmp%1").arg(task->getRealPrecision())); + break; + case Task::ExternalToolMode: + case Task::SpecialJudgeMode: + default: + ui->specialJudge->setChecked(true); + break; + } +} + +void ExportingAsUojFormat::specialJudgeChanged(int new_state) { + if (new_state) { + ui->checker->setFilters(QDir::Files); + } else { + ui->checker->setFilters(QDir::Filters()); + } + ui->checker->refreshFileList(); +} + +void ExportingAsUojFormat::exportData() { + QString exportFolder = "data_uoj_format/" + ui->problemTitle->text(); + + QDir exportLoca; + if (exportLoca.exists(exportFolder)) { + exportLoca = QDir(exportFolder); + if (! exportLoca.removeRecursively()) { + QMessageBox::information(this, tr("Export as UOJ Format"), + tr("Aborted: Cannot remove path `%1'.").arg(exportFolder)); + return; + } + } + + exportLoca = QDir(); + if (! exportLoca.mkpath(exportFolder)) { + QMessageBox::information(this, tr("Export as UOJ Format"), + tr("Aborted: Cannot make path `%1'.").arg(exportFolder)); + return; + } + + exportLoca = QDir(exportFolder); + QFile problemConfigFile(exportLoca.filePath("problem.conf"), this); + if (! problemConfigFile.open(QFile::WriteOnly | QFile::Truncate)) { + QMessageBox::information(this, tr("Export as UOJ Format"), tr("Aborted: Cannot open problem.conf.")); + return; + } + QTextStream problemConfig(&problemConfigFile); + + QString inputFilePrefix = ui->inputFilePrefix->text(); + QString outputFilePrefix = ui->outputFilePrefix->text(); + QString inputFileSuffix = ui->inputFileSuffix->text(); + QString outputFileSuffix = ui->outputFileSuffix->text(); + int numberOfTests = 0; + QDir dataPath(Settings::dataPath()); + for (auto testcase : task->getTestCaseList()) { + for (int i = 0; i < testcase->getInputFiles().length(); ++i) { + ++numberOfTests; + QString inputFileName = testcase->getInputFiles()[i], + outputFileName = testcase->getOutputFiles()[i]; + QString newInputFileName = + inputFilePrefix + QString::number(numberOfTests) + "." + inputFileSuffix, + newOutputFileName = + outputFilePrefix + QString::number(numberOfTests) + "." + outputFileSuffix; + if (! copyFile(dataPath.filePath(inputFileName), exportLoca.filePath(newInputFileName))) + return; + if (! copyFile(dataPath.filePath(outputFileName), exportLoca.filePath(newOutputFileName))) + return; + } + } + + problemConfig << "use_builtin_judger on" << Qt::endl; + if (! ui->specialJudge->isChecked()) { + problemConfig << "use_builtin_checker " << ui->checker->text() << Qt::endl; + } else if (ui->checker->text().length()) { + if (! copyFile(dataPath.filePath(ui->checker->text()), exportLoca.filePath("chk.cpp"))) + return; + } + + problemConfig << "n_tests " << numberOfTests << Qt::endl; + problemConfig << "n_ex_tests 0" << Qt::endl; + problemConfig << "n_sample_tests 0" << Qt::endl; + + problemConfig << "input_pre " << ui->inputFilePrefix->text() << Qt::endl; + problemConfig << "input_suf " << ui->inputFileSuffix->text() << Qt::endl; + problemConfig << "output_pre " << ui->outputFilePrefix->text() << Qt::endl; + problemConfig << "output_suf " << ui->outputFileSuffix->text() << Qt::endl; + + problemConfig << "time_limit " << ui->timeLimit->text() << Qt::endl; + problemConfig << "memory_limit " << ui->memoryLimit->text() << Qt::endl; + problemConfig << "output_limit " << 64 << Qt::endl; + + bool subtask = false; + for (auto testcase : task->getTestCaseList()) { + if (testcase->getInputFiles().length() != 1 || ! testcase->getDependenceSubtask().empty()) + subtask = true; + } + + if (subtask) { + int subtaskEnd = 0, subtaskId = 0; + problemConfig << "n_subtasks " << task->getTestCaseList().length() << Qt::endl; + for (auto testcase : task->getTestCaseList()) { + ++subtaskId; + subtaskEnd += testcase->getInputFiles().length(); + problemConfig << "subtask_end_" << subtaskId << " " << subtaskEnd << Qt::endl; + problemConfig << "subtask_score_" << subtaskId << " " << testcase->getFullScore() << Qt::endl; + if (auto dependences = testcase->getDependenceSubtask(); ! dependences.empty()) { + problemConfig << "subtask_dependence_" << subtaskId << " many" << Qt::endl; + int dependenceId = 0; + for (auto i : dependences) { + ++dependenceId; + problemConfig << "subtask_dependence_" << subtaskId << "_" << dependenceId << " " << i + << Qt::endl; + } + } + } + } else { + int testcaseId = 0; + for (auto testcase : task->getTestCaseList()) { + ++testcaseId; + problemConfig << "point_score_" << testcaseId << " " << testcase->getFullScore() << Qt::endl; + } + } +} + +bool ExportingAsUojFormat::copyFile(QString from, QString to) { + if (! QFile::copy(from, to)) { + QMessageBox::information(this, tr("Export as UOJ Format"), + tr("Aborted: Cannot copy file `%1' to `%2'.").arg(from, to)); + return false; + } + return true; +} diff --git a/src/exportingasuojformat.h b/src/exportingasuojformat.h new file mode 100644 index 00000000..eeecb879 --- /dev/null +++ b/src/exportingasuojformat.h @@ -0,0 +1,32 @@ +#ifndef EXPORTINGASUOJFORMAT_H +#define EXPORTINGASUOJFORMAT_H + +#include +#include + +namespace Ui { + class ExportingAsUojFormat; +} + +class Task; + +class ExportingAsUojFormat : public QDialog { + Q_OBJECT + + public: + explicit ExportingAsUojFormat(QWidget *parent = nullptr); + ~ExportingAsUojFormat(); + + void init(Task *task); + void exportData(); + + public slots: + void specialJudgeChanged(int); + + private: + Ui::ExportingAsUojFormat *ui; + Task *task; + bool copyFile(QString, QString); +}; + +#endif // EXPORTINGASUOJFORMAT_H diff --git a/src/forms/exportingasuojformat.ui b/src/forms/exportingasuojformat.ui new file mode 100644 index 00000000..dfd4aae2 --- /dev/null +++ b/src/forms/exportingasuojformat.ui @@ -0,0 +1,303 @@ + + + ExportingAsUojFormat + + + + 0 + 0 + 390 + 320 + + + + + 10 + + + + Dialog + + + + + 20 + 280 + 341 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + 10 + 10 + 151 + 21 + + + + Problem Title + + + + + + 170 + 10 + 211 + 21 + + + + + + + 10 + 40 + 151 + 21 + + + + Input File Prefix + + + + + + 170 + 40 + 211 + 21 + + + + + + + 10 + 70 + 151 + 21 + + + + Input File Suffix + + + + + + 170 + 70 + 211 + 21 + + + + + + + 10 + 100 + 151 + 21 + + + + Output File Prefix + + + + + + 170 + 100 + 211 + 21 + + + + + + + 10 + 130 + 151 + 21 + + + + Output File Suffix + + + + + + 170 + 130 + 211 + 21 + + + + + + + 10 + 160 + 151 + 21 + + + + Time Limit + + + + + + 170 + 160 + 181 + 21 + + + + + + + 360 + 160 + 21 + 21 + + + + s + + + + + + 360 + 190 + 21 + 21 + + + + MB + + + + + + 170 + 190 + 181 + 21 + + + + + + + 10 + 190 + 151 + 21 + + + + Memory Limit + + + + + + 10 + 220 + 151 + 21 + + + + Checker + + + + + + 170 + 220 + 211 + 21 + + + + + + + 170 + 250 + 211 + 16 + + + + Special Judge + + + + + + FileLineEdit + QLineEdit +
filelineedit.h
+
+
+ + + + buttonBox + accepted() + ExportingAsUojFormat + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ExportingAsUojFormat + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/src/summarytree.cpp b/src/summarytree.cpp index 7be2b185..78c6081a 100644 --- a/src/summarytree.cpp +++ b/src/summarytree.cpp @@ -14,6 +14,7 @@ #include "core/contest.h" #include "core/task.h" #include "core/testcase.h" +#include "exportingasuojformat.h" #include "exttestcasemodifierdialog.h" SummaryTree::SummaryTree(QWidget *parent) : QTreeWidget(parent) { @@ -25,6 +26,7 @@ SummaryTree::SummaryTree(QWidget *parent) : QTreeWidget(parent) { deleteTaskAction = new QAction(tr("Delete Current Task"), this); deleteTestCaseAction = new QAction(tr("Delete Current Test Case"), this); ExtTestCaseModifierAction = new QAction(tr("Advanced Test Case Modifier"), this); + exportAsUojFormatAction = new QAction(tr("Export as UOJ Format"), this); addTaskKeyAction = new QAction(this); addTestCaseKeyAction = new QAction(this); deleteTaskKeyAction = new QAction(this); @@ -49,6 +51,7 @@ SummaryTree::SummaryTree(QWidget *parent) : QTreeWidget(parent) { connect(addTestCaseAction, &QAction::triggered, this, &SummaryTree::addTestCase); connect(addTestCasesAction, &QAction::triggered, this, &SummaryTree::addTestCases); connect(ExtTestCaseModifierAction, &QAction::triggered, this, &SummaryTree::launchExtTestCaseModifier); + connect(exportAsUojFormatAction, &QAction::triggered, this, &SummaryTree::exportAsUojFormat); connect(addTaskKeyAction, &QAction::triggered, this, &SummaryTree::addTask); connect(addTestCaseKeyAction, &QAction::triggered, this, &SummaryTree::addTestCase); connect(deleteTaskAction, &QAction::triggered, this, &SummaryTree::deleteTask); @@ -69,6 +72,8 @@ void SummaryTree::changeEvent(QEvent *event) { QApplication::translate("SummaryTree", "Delete Current Test Case", nullptr)); ExtTestCaseModifierAction->setText( QApplication::translate("SummaryTree", "Advanced Test Case Modifier", nullptr)); + exportAsUojFormatAction->setText( + QApplication::translate("SummaryTree", "Export as UOJ Format", nullptr)); for (int i = 0; i < topLevelItemCount(); i++) { QTreeWidgetItem *taskItem = topLevelItem(i); @@ -142,6 +147,8 @@ void SummaryTree::contextMenuEvent(QContextMenuEvent * /*event*/) { contextMenu->addAction(addTestCasesAction); contextMenu->addSeparator(); contextMenu->addAction(ExtTestCaseModifierAction); + contextMenu->addSeparator(); + contextMenu->addAction(exportAsUojFormatAction); contextMenu->exec(QCursor::pos()); delete contextMenu; } else { @@ -340,3 +347,27 @@ void SummaryTree::launchExtTestCaseModifier() { setContest(curContest); } + +void SummaryTree::exportAsUojFormat() { + QTreeWidgetItem *curItem = currentItem(); + + if (indexOfTopLevelItem(curItem) == -1) { + curItem = curItem->parent(); + } + + int index = indexOfTopLevelItem(curItem); + Task *curTask = curContest->getTask(index); + + if (curTask->getTaskType() != Task::Traditional) { + QMessageBox::information(this, tr("Export as UOJ Format"), tr("Task Type Not Supported")); + return; + } + + auto *dialog = new ExportingAsUojFormat; + + dialog->init(curTask); + + if (dialog->exec() == QDialog::Accepted) { + dialog->exportData(); + } +} diff --git a/src/summarytree.h b/src/summarytree.h index 781172e2..0534c2ba 100644 --- a/src/summarytree.h +++ b/src/summarytree.h @@ -44,6 +44,7 @@ class SummaryTree : public QTreeWidget { QAction *deleteTaskKeyAction; QAction *deleteTestCaseKeyAction; QAction *ExtTestCaseModifierAction; + QAction *exportAsUojFormatAction; private slots: void addTask(); @@ -55,6 +56,7 @@ class SummaryTree : public QTreeWidget { void itemChanged(QTreeWidgetItem *); void titleChanged(const QString &); void launchExtTestCaseModifier(); + void exportAsUojFormat(); signals: void taskChanged(); diff --git a/translations/en_US.ts b/translations/en_US.ts index 9721c39f..a746cc3a 100644 --- a/translations/en_US.ts +++ b/translations/en_US.ts @@ -2251,6 +2251,18 @@ p, li { white-space: pre-wrap; } Advanced Test Case Modifier + + Export as UOJ Format + + + + Task Type Not Supported + + + + Task Type Not Supported + + Test Case #%1 @@ -2682,4 +2694,63 @@ p, li { white-space: pre-wrap; } + + ExportingAsUojFormat + + Export as UOJ Format + + + + Problem Title + + + + Input File Prefix + + + + Input File Suffix + + + + Output File Prefix + + + + Output File Suffix + + + + Time Limit + + + + Memory Limit + + + + Checker + + + + Special Judge + + + + Aborted: Cannot remove path `%1'. + + + + Aborted: Cannot make path `%1'. + + + + Aborted: Cannot open problem.conf. + + + + Aborted: Cannot copy file `%1' to `%2'. + + + diff --git a/translations/zh_CN.ts b/translations/zh_CN.ts index fe9e1066..c3fe1776 100755 --- a/translations/zh_CN.ts +++ b/translations/zh_CN.ts @@ -2282,6 +2282,14 @@ p, li { white-space: pre-wrap; } Advanced Test Case Modifier 增强测试点调整器 + + Export as UOJ Format + 导出为 UOJ 格式 + + + Task Type Not Supported + 不支持的题目类型 + Test Case #%1 测试点 #%1 @@ -2716,4 +2724,63 @@ p, li { white-space: pre-wrap; } 新建 + + ExportingAsUojFormat + + Export as UOJ Format + 导出为 UOJ 格式 + + + Problem Title + 题目标题 + + + Input File Prefix + 输入文件名 + + + Input File Suffix + 输入扩展名 + + + Output File Prefix + 输出文件名 + + + Output File Suffix + 输出扩展名 + + + Time Limit + 时间限制 + + + Memory Limit + 空间限制 + + + Checker + 校验器 + + + Special Judge + 自定义校验器 + + + Aborted: Cannot remove path `%1'. + 导出失败:无法删除 `%1' + + + Aborted: Cannot make path `%1'. + 导出失败:无法创建 `%1' + + + Aborted: Cannot open problem.conf. + 导出失败:无法打开 problem.conf + + + Aborted: Cannot copy file `%1' to `%2'. + 导出失败: 无法将 `%1' 复制到 `%2'. + + From 48e93bdbb3bbdb22c5d89830fa621aeb51e0c976 Mon Sep 17 00:00:00 2001 From: Wallbreaker5th <3277946283@qq.com> Date: Sat, 11 Sep 2021 14:51:05 +0800 Subject: [PATCH 2/2] Use grid layout --- src/forms/exportingasuojformat.ui | 352 ++++++++++-------------------- 1 file changed, 113 insertions(+), 239 deletions(-) diff --git a/src/forms/exportingasuojformat.ui b/src/forms/exportingasuojformat.ui index dfd4aae2..970ff016 100644 --- a/src/forms/exportingasuojformat.ui +++ b/src/forms/exportingasuojformat.ui @@ -18,245 +18,119 @@ Dialog - - - - 20 - 280 - 341 - 32 - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - 10 - 10 - 151 - 21 - - - - Problem Title - - - - - - 170 - 10 - 211 - 21 - - - - - - - 10 - 40 - 151 - 21 - - - - Input File Prefix - - - - - - 170 - 40 - 211 - 21 - - - - - - - 10 - 70 - 151 - 21 - - - - Input File Suffix - - - - - - 170 - 70 - 211 - 21 - - - - - - - 10 - 100 - 151 - 21 - - - - Output File Prefix - - - - - - 170 - 100 - 211 - 21 - - - - - - - 10 - 130 - 151 - 21 - - - - Output File Suffix - - - - - - 170 - 130 - 211 - 21 - - - - - - - 10 - 160 - 151 - 21 - - - - Time Limit - - - - - - 170 - 160 - 181 - 21 - - - - - - - 360 - 160 - 21 - 21 - - - - s - - - - - - 360 - 190 - 21 - 21 - - - - MB - - - - - - 170 - 190 - 181 - 21 - - - - - - - 10 - 190 - 151 - 21 - - - - Memory Limit - - - - - - 10 - 220 - 151 - 21 - - - - Checker - - - - - - 170 - 220 - 211 - 21 - - - - - - - 170 - 250 - 211 - 16 - - - - Special Judge - - + + + + + Problem Title + + + + + + + + + + Input File Prefix + + + + + + + + + + Input File Suffix + + + + + + + + + + Output File Prefix + + + + + + + + + + Output File Suffix + + + + + + + + + + Time Limit + + + + + + + + + + s + + + + + + + Memory Limit + + + + + + + + + + MB + + + + + + + Checker + + + + + + + + + + Special Judge + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + +