mirror of
https://github.com/jie65535/GrasscutterCommandGenerator.git
synced 2025-10-20 19:39:47 +08:00
Add Task Page (#166)
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
@@ -51,7 +52,7 @@ namespace GrasscutterTools.Pages
|
||||
/// <param name="args">参数</param>
|
||||
public SetCommandHandler SetCommand { get; set; }
|
||||
|
||||
public delegate Task<bool> RunCommandsHandler(string[] commands);
|
||||
public delegate Task<bool> RunCommandsHandler(params string[] commands);
|
||||
|
||||
/// <summary>
|
||||
/// 运行命令
|
||||
@@ -63,6 +64,30 @@ namespace GrasscutterTools.Pages
|
||||
/// </summary>
|
||||
public Func<string> GetCommand { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 运行原始命令(未处理的竖线分割命令文本)
|
||||
/// </summary>
|
||||
/// <param name="commands">未处理的竖线分割命令文本</param>
|
||||
/// <returns>是否运行成功</returns>
|
||||
protected async Task<bool> RunRawCommands(string commands)
|
||||
{
|
||||
if (commands.IndexOf('|') == -1)
|
||||
return await RunCommands(FormatCommand(commands));
|
||||
else
|
||||
return await RunCommands(commands.Split('|').Select(it => FormatCommand(it)).ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 格式化命令
|
||||
/// (去除收尾空白,替换换行)
|
||||
/// </summary>
|
||||
/// <param name="raw">原始输入</param>
|
||||
/// <returns>格式化后可执行命令</returns>
|
||||
private string FormatCommand(string raw)
|
||||
{
|
||||
return raw.Trim().Replace("\\r", "\r").Replace("\\n", "\n");
|
||||
}
|
||||
|
||||
#endregion - 命令相关 -
|
||||
|
||||
#region - 生命周期事件 -
|
||||
|
250
Source/GrasscutterTools/Pages/PageTasks.Designer.cs
generated
Normal file
250
Source/GrasscutterTools/Pages/PageTasks.Designer.cs
generated
Normal file
@@ -0,0 +1,250 @@
|
||||
namespace GrasscutterTools.Pages
|
||||
{
|
||||
partial class PageTasks
|
||||
{
|
||||
/// <summary>
|
||||
/// 必需的设计器变量。
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// 清理所有正在使用的资源。
|
||||
/// </summary>
|
||||
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region 组件设计器生成的代码
|
||||
|
||||
/// <summary>
|
||||
/// 设计器支持所需的方法 - 不要修改
|
||||
/// 使用代码编辑器修改此方法的内容。
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.GrpTasks = new System.Windows.Forms.GroupBox();
|
||||
this.ListTasks = new System.Windows.Forms.ListView();
|
||||
this.ColTag = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.ColContent = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.ColDelay = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.ColCount = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.GrpTask = new System.Windows.Forms.GroupBox();
|
||||
this.BtnRemove = new System.Windows.Forms.Button();
|
||||
this.BtnAccept = new System.Windows.Forms.Button();
|
||||
this.NUDTriggerCount = new System.Windows.Forms.NumericUpDown();
|
||||
this.LblTriggerCount = new System.Windows.Forms.Label();
|
||||
this.DTPDelay = new System.Windows.Forms.DateTimePicker();
|
||||
this.LblDelay = new System.Windows.Forms.Label();
|
||||
this.TxtTag = new System.Windows.Forms.TextBox();
|
||||
this.LblTag = new System.Windows.Forms.Label();
|
||||
this.GrpTasks.SuspendLayout();
|
||||
this.GrpTask.SuspendLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.NUDTriggerCount)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// GrpTasks
|
||||
//
|
||||
this.GrpTasks.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.GrpTasks.Controls.Add(this.ListTasks);
|
||||
this.GrpTasks.Location = new System.Drawing.Point(3, 3);
|
||||
this.GrpTasks.Name = "GrpTasks";
|
||||
this.GrpTasks.Size = new System.Drawing.Size(640, 183);
|
||||
this.GrpTasks.TabIndex = 0;
|
||||
this.GrpTasks.TabStop = false;
|
||||
this.GrpTasks.Text = "任务列表";
|
||||
//
|
||||
// ListTasks
|
||||
//
|
||||
this.ListTasks.AllowColumnReorder = true;
|
||||
this.ListTasks.CheckBoxes = true;
|
||||
this.ListTasks.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
|
||||
this.ColTag,
|
||||
this.ColContent,
|
||||
this.ColDelay,
|
||||
this.ColCount});
|
||||
this.ListTasks.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.ListTasks.HideSelection = false;
|
||||
this.ListTasks.Location = new System.Drawing.Point(3, 19);
|
||||
this.ListTasks.MultiSelect = false;
|
||||
this.ListTasks.Name = "ListTasks";
|
||||
this.ListTasks.Size = new System.Drawing.Size(634, 161);
|
||||
this.ListTasks.TabIndex = 0;
|
||||
this.ListTasks.UseCompatibleStateImageBehavior = false;
|
||||
this.ListTasks.View = System.Windows.Forms.View.Details;
|
||||
this.ListTasks.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.ListTasks_ItemChecked);
|
||||
this.ListTasks.SelectedIndexChanged += new System.EventHandler(this.ListTasks_SelectedIndexChanged);
|
||||
//
|
||||
// ColTag
|
||||
//
|
||||
this.ColTag.Text = "标签";
|
||||
this.ColTag.Width = 150;
|
||||
//
|
||||
// ColContent
|
||||
//
|
||||
this.ColContent.Text = "内容";
|
||||
this.ColContent.Width = 330;
|
||||
//
|
||||
// ColDelay
|
||||
//
|
||||
this.ColDelay.Text = "延迟";
|
||||
//
|
||||
// ColCount
|
||||
//
|
||||
this.ColCount.Text = "次数";
|
||||
//
|
||||
// GrpTask
|
||||
//
|
||||
this.GrpTask.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.GrpTask.Controls.Add(this.BtnRemove);
|
||||
this.GrpTask.Controls.Add(this.BtnAccept);
|
||||
this.GrpTask.Controls.Add(this.NUDTriggerCount);
|
||||
this.GrpTask.Controls.Add(this.LblTriggerCount);
|
||||
this.GrpTask.Controls.Add(this.DTPDelay);
|
||||
this.GrpTask.Controls.Add(this.LblDelay);
|
||||
this.GrpTask.Controls.Add(this.TxtTag);
|
||||
this.GrpTask.Controls.Add(this.LblTag);
|
||||
this.GrpTask.Location = new System.Drawing.Point(3, 186);
|
||||
this.GrpTask.Name = "GrpTask";
|
||||
this.GrpTask.Size = new System.Drawing.Size(640, 50);
|
||||
this.GrpTask.TabIndex = 1;
|
||||
this.GrpTask.TabStop = false;
|
||||
this.GrpTask.Text = "任务";
|
||||
//
|
||||
// BtnRemove
|
||||
//
|
||||
this.BtnRemove.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.BtnRemove.Location = new System.Drawing.Point(534, 18);
|
||||
this.BtnRemove.Name = "BtnRemove";
|
||||
this.BtnRemove.Size = new System.Drawing.Size(100, 23);
|
||||
this.BtnRemove.TabIndex = 7;
|
||||
this.BtnRemove.Text = "× 删除";
|
||||
this.BtnRemove.UseVisualStyleBackColor = true;
|
||||
this.BtnRemove.Click += new System.EventHandler(this.BtnRemove_Click);
|
||||
//
|
||||
// BtnAccept
|
||||
//
|
||||
this.BtnAccept.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.BtnAccept.Location = new System.Drawing.Point(428, 18);
|
||||
this.BtnAccept.Name = "BtnAccept";
|
||||
this.BtnAccept.Size = new System.Drawing.Size(100, 23);
|
||||
this.BtnAccept.TabIndex = 6;
|
||||
this.BtnAccept.Text = "√ 确定";
|
||||
this.BtnAccept.UseVisualStyleBackColor = true;
|
||||
this.BtnAccept.Click += new System.EventHandler(this.BtnAccept_Click);
|
||||
//
|
||||
// NUDTriggerCount
|
||||
//
|
||||
this.NUDTriggerCount.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.NUDTriggerCount.Location = new System.Drawing.Point(372, 19);
|
||||
this.NUDTriggerCount.Maximum = new decimal(new int[] {
|
||||
1000,
|
||||
0,
|
||||
0,
|
||||
0});
|
||||
this.NUDTriggerCount.Minimum = new decimal(new int[] {
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
-2147483648});
|
||||
this.NUDTriggerCount.Name = "NUDTriggerCount";
|
||||
this.NUDTriggerCount.Size = new System.Drawing.Size(50, 23);
|
||||
this.NUDTriggerCount.TabIndex = 5;
|
||||
this.NUDTriggerCount.Value = new decimal(new int[] {
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
-2147483648});
|
||||
//
|
||||
// LblTriggerCount
|
||||
//
|
||||
this.LblTriggerCount.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.LblTriggerCount.AutoSize = true;
|
||||
this.LblTriggerCount.Location = new System.Drawing.Point(334, 22);
|
||||
this.LblTriggerCount.Name = "LblTriggerCount";
|
||||
this.LblTriggerCount.Size = new System.Drawing.Size(32, 17);
|
||||
this.LblTriggerCount.TabIndex = 4;
|
||||
this.LblTriggerCount.Text = "次数";
|
||||
//
|
||||
// DTPDelay
|
||||
//
|
||||
this.DTPDelay.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.DTPDelay.CustomFormat = "";
|
||||
this.DTPDelay.Format = System.Windows.Forms.DateTimePickerFormat.Time;
|
||||
this.DTPDelay.Location = new System.Drawing.Point(258, 19);
|
||||
this.DTPDelay.Name = "DTPDelay";
|
||||
this.DTPDelay.ShowUpDown = true;
|
||||
this.DTPDelay.Size = new System.Drawing.Size(70, 23);
|
||||
this.DTPDelay.TabIndex = 3;
|
||||
this.DTPDelay.Value = new System.DateTime(2022, 12, 10, 0, 1, 0, 0);
|
||||
//
|
||||
// LblDelay
|
||||
//
|
||||
this.LblDelay.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.LblDelay.AutoSize = true;
|
||||
this.LblDelay.Location = new System.Drawing.Point(220, 22);
|
||||
this.LblDelay.Name = "LblDelay";
|
||||
this.LblDelay.Size = new System.Drawing.Size(32, 17);
|
||||
this.LblDelay.TabIndex = 2;
|
||||
this.LblDelay.Text = "延迟";
|
||||
//
|
||||
// TxtTag
|
||||
//
|
||||
this.TxtTag.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.TxtTag.Location = new System.Drawing.Point(49, 18);
|
||||
this.TxtTag.Name = "TxtTag";
|
||||
this.TxtTag.Size = new System.Drawing.Size(165, 23);
|
||||
this.TxtTag.TabIndex = 1;
|
||||
//
|
||||
// LblTag
|
||||
//
|
||||
this.LblTag.AutoSize = true;
|
||||
this.LblTag.Location = new System.Drawing.Point(11, 21);
|
||||
this.LblTag.Name = "LblTag";
|
||||
this.LblTag.Size = new System.Drawing.Size(32, 17);
|
||||
this.LblTag.TabIndex = 0;
|
||||
this.LblTag.Text = "标签";
|
||||
//
|
||||
// PageTasks
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.GrpTask);
|
||||
this.Controls.Add(this.GrpTasks);
|
||||
this.Name = "PageTasks";
|
||||
this.GrpTasks.ResumeLayout(false);
|
||||
this.GrpTask.ResumeLayout(false);
|
||||
this.GrpTask.PerformLayout();
|
||||
((System.ComponentModel.ISupportInitialize)(this.NUDTriggerCount)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.GroupBox GrpTasks;
|
||||
private System.Windows.Forms.GroupBox GrpTask;
|
||||
private System.Windows.Forms.DateTimePicker DTPDelay;
|
||||
private System.Windows.Forms.Label LblDelay;
|
||||
private System.Windows.Forms.TextBox TxtTag;
|
||||
private System.Windows.Forms.Label LblTag;
|
||||
private System.Windows.Forms.NumericUpDown NUDTriggerCount;
|
||||
private System.Windows.Forms.Label LblTriggerCount;
|
||||
private System.Windows.Forms.Button BtnRemove;
|
||||
private System.Windows.Forms.Button BtnAccept;
|
||||
private System.Windows.Forms.ListView ListTasks;
|
||||
private System.Windows.Forms.ColumnHeader ColTag;
|
||||
private System.Windows.Forms.ColumnHeader ColContent;
|
||||
private System.Windows.Forms.ColumnHeader ColDelay;
|
||||
private System.Windows.Forms.ColumnHeader ColCount;
|
||||
}
|
||||
}
|
265
Source/GrasscutterTools/Pages/PageTasks.cs
Normal file
265
Source/GrasscutterTools/Pages/PageTasks.cs
Normal file
@@ -0,0 +1,265 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using GrasscutterTools.Properties;
|
||||
using GrasscutterTools.Utils;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace GrasscutterTools.Pages
|
||||
{
|
||||
internal partial class PageTasks : BasePage
|
||||
{
|
||||
private const string TAG = nameof(PageTasks);
|
||||
|
||||
public PageTasks()
|
||||
{
|
||||
InitializeComponent();
|
||||
ListTasks.FullRowSelect = true;
|
||||
if (DesignMode) return;
|
||||
|
||||
InitTasks();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 循环任务
|
||||
/// </summary>
|
||||
private class LoopTask
|
||||
{
|
||||
/// <summary>
|
||||
/// 任务标签
|
||||
/// </summary>
|
||||
public string Tag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 任务内容
|
||||
/// </summary>
|
||||
public string Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 延迟时间(秒)
|
||||
/// </summary>
|
||||
public int DelayS { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 触发次数 -1为无限循环
|
||||
/// </summary>
|
||||
public int TriggerCount { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 任务列表路径
|
||||
/// </summary>
|
||||
private readonly string TasksJsonPath = Common.GetAppDataFile("Tasks.json");
|
||||
|
||||
/// <summary>
|
||||
/// 任务集合
|
||||
/// </summary>
|
||||
private List<LoopTask> Tasks;
|
||||
|
||||
/// <summary>
|
||||
/// 运行中的任务集合
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<string, CancellationTokenSource> RunningTasks = new ConcurrentDictionary<string, CancellationTokenSource>();
|
||||
|
||||
/// <summary>
|
||||
/// 初始化任务集合
|
||||
/// </summary>
|
||||
private void InitTasks()
|
||||
{
|
||||
if (File.Exists(TasksJsonPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
Tasks = JsonConvert.DeserializeObject<List<LoopTask>>(File.ReadAllText(TasksJsonPath));
|
||||
ListTasks.Items.AddRange(Tasks.Select(t => TaskToViewItem(t)).ToArray());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Tasks = new List<LoopTask>();
|
||||
Logger.W(TAG, "Parsing Tasks json failed", ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Tasks = new List<LoopTask>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭时触发
|
||||
/// </summary>
|
||||
public override void OnClosed()
|
||||
{
|
||||
// 取消所有正在运行的任务
|
||||
foreach (var cs in RunningTasks.Values)
|
||||
cs.Cancel();
|
||||
// 清空列表
|
||||
RunningTasks.Clear();
|
||||
|
||||
// 保存任务列表
|
||||
File.WriteAllText(TasksJsonPath, JsonConvert.SerializeObject(Tasks));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 任务转为列表项
|
||||
/// </summary>
|
||||
/// <param name="task">任务</param>
|
||||
/// <returns>列表项</returns>
|
||||
private static ListViewItem TaskToViewItem(LoopTask task) => new ListViewItem(new string[]
|
||||
{
|
||||
task.Tag,
|
||||
task.Content,
|
||||
TimeSpan.FromSeconds(task.DelayS).ToString(),
|
||||
task.TriggerCount.ToString(),
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// 列表选中项改变时触发
|
||||
/// </summary>
|
||||
private void ListTasks_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (ListTasks.SelectedIndices.Count == 0) return;
|
||||
int i = ListTasks.SelectedIndices[0];
|
||||
var task = Tasks[i];
|
||||
TxtTag.Text = task.Tag;
|
||||
DTPDelay.Value = DateTime.Today.Add(TimeSpan.FromSeconds(task.DelayS));
|
||||
NUDTriggerCount.Value = task.TriggerCount;
|
||||
// 设置命令
|
||||
SetCommand(task.Content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 点击确定按钮时触发
|
||||
/// </summary>
|
||||
private void BtnAccept_Click(object sender, EventArgs e)
|
||||
{
|
||||
var tag = TxtTag.Text.Trim();
|
||||
var commands = GetCommand();
|
||||
var delay = DTPDelay.Value.TimeOfDay;
|
||||
var count = (int)NUDTriggerCount.Value;
|
||||
if (string.IsNullOrEmpty(tag) || string.IsNullOrEmpty(commands) || delay.Ticks == 0)
|
||||
{
|
||||
MessageBox.Show(Resources.EmptyInputTip, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找是否已经存在
|
||||
var i = Tasks.FindIndex(t => t.Tag == tag);
|
||||
if (i == -1)
|
||||
{
|
||||
// 新建任务
|
||||
var t = new LoopTask
|
||||
{
|
||||
Tag = tag,
|
||||
Content = commands,
|
||||
DelayS = (int)delay.TotalSeconds,
|
||||
TriggerCount = count,
|
||||
};
|
||||
ListTasks.Items.Add(TaskToViewItem(t));
|
||||
Tasks.Add(t);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 已存在的任务,确认是否正在运行中
|
||||
if (ListTasks.Items[i].Checked || RunningTasks.ContainsKey(tag))
|
||||
{
|
||||
MessageBox.Show(Resources.TaskRunningCannotOperated, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// 否则修改任务内容
|
||||
var task = Tasks[i];
|
||||
task.Content = commands;
|
||||
task.DelayS = (int)delay.TotalSeconds;
|
||||
task.TriggerCount = count;
|
||||
ListTasks.Items[i] = TaskToViewItem(task);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 点击删除按钮时触发
|
||||
/// </summary>
|
||||
private void BtnRemove_Click(object sender, EventArgs e)
|
||||
{
|
||||
var tag = TxtTag.Text.Trim();
|
||||
// 查找是否已经存在
|
||||
var i = Tasks.FindIndex(t => t.Tag == tag);
|
||||
if (i == -1) return;
|
||||
|
||||
if (ListTasks.Items[i].Checked || RunningTasks.ContainsKey(tag))
|
||||
{
|
||||
MessageBox.Show(Resources.TaskRunningCannotOperated, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// 删除实例
|
||||
ListTasks.Items.RemoveAt(i);
|
||||
Tasks.RemoveAt(i);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 任务前复选框改变时触发
|
||||
/// </summary>
|
||||
private void ListTasks_ItemChecked(object sender, ItemCheckedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var item = e.Item;
|
||||
if (item.Index < 0 || item.Index >= Tasks.Count)
|
||||
return;
|
||||
var task = Tasks[item.Index];
|
||||
// 先将旧的任务取消
|
||||
if (RunningTasks.TryRemove(task.Tag, out var source))
|
||||
source.Cancel();
|
||||
|
||||
if (item.Checked)
|
||||
{
|
||||
var cancelSource = new CancellationTokenSource();
|
||||
RunningTasks.TryAdd(task.Tag, cancelSource);
|
||||
var token = cancelSource.Token;
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.I(TAG, $"Task \"{task.Tag}\" started");
|
||||
// 循环执行命令
|
||||
for (int c = 0;
|
||||
!token.IsCancellationRequested
|
||||
&& (c < task.TriggerCount || task.TriggerCount <= 0);
|
||||
c++)
|
||||
{
|
||||
// 延迟
|
||||
await Task.Delay(task.DelayS * 1000, token);
|
||||
// 使用UI线程执行
|
||||
var ret = Invoke(new Func<string, Task<bool>>(RunRawCommands), task.Content);
|
||||
if (ret is Task<bool> b && b.Result == false)
|
||||
break;
|
||||
// 执行
|
||||
//if (!await RunRawCommands(task.Content))
|
||||
// break;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 任务结束后取消勾选状态
|
||||
BeginInvoke(new Action(() => item.Checked = false));
|
||||
Logger.I(TAG, $"Task \"{task.Tag}\" stoped");
|
||||
}
|
||||
}, token);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.E(TAG, "Start or Stop Task failed.", ex);
|
||||
MessageBox.Show(ex.ToString(), Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
120
Source/GrasscutterTools/Pages/PageTasks.resx
Normal file
120
Source/GrasscutterTools/Pages/PageTasks.resx
Normal file
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
Reference in New Issue
Block a user