update:新建多人跳绳表&新建跳远位置点表&跳远配置页面&管理员密码输入
This commit is contained in:
parent
68078a2294
commit
ec7b800c26
@ -19,6 +19,7 @@ namespace AiSportsMicrospaceDB.DBContext
|
|||||||
public DbSet<BasicConfig> BasicConfig { get; set; }
|
public DbSet<BasicConfig> BasicConfig { get; set; }
|
||||||
|
|
||||||
public DbSet<SixGroupPoints> SixGroupPoints { get; set; }
|
public DbSet<SixGroupPoints> SixGroupPoints { get; set; }
|
||||||
|
public DbSet<JumpLongPoints> JumpLongPoints { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
25
AiSportsMicrospaceDB/Entities/JumpLongPoints.cs
Normal file
25
AiSportsMicrospaceDB/Entities/JumpLongPoints.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace AiSportsMicrospaceDB.Entities
|
||||||
|
{
|
||||||
|
|
||||||
|
public class JumpLongPoints {
|
||||||
|
// ✅ 主键
|
||||||
|
public int Id { get; set; }
|
||||||
|
public Point TopPoint1 { get; set; }
|
||||||
|
public Point TopPoint2 { get; set; }
|
||||||
|
public Point TopPoint3 { get; set; }
|
||||||
|
public Point BottomPoint1 { get; set; }
|
||||||
|
public Point BottomPoint2 { get; set; }
|
||||||
|
public Point BottomPoint3 { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
<Application x:Class="Wpf_AiSportsMicrospace.App"
|
<Application x:Class="Wpf_AiSportsMicrospace.App"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||||
xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.Views"
|
xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.Views"
|
||||||
xmlns:conv="clr-namespace:Wpf_AiSportsMicrospace.Converter">
|
xmlns:conv="clr-namespace:Wpf_AiSportsMicrospace.Converter">
|
||||||
|
|
||||||
@ -8,12 +9,33 @@
|
|||||||
|
|
||||||
<!--StartupUri="Views/JumpRope/GroupJumpRope.xaml">-->
|
<!--StartupUri="Views/JumpRope/GroupJumpRope.xaml">-->
|
||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
<FontFamily x:Key="AppFont">./Resoures/Fonts/myFontFamily</FontFamily>
|
<ResourceDictionary>
|
||||||
<Style TargetType="TextBlock">
|
<!-- 1. 先合并 HandyControl 资源 -->
|
||||||
<Setter Property="FontFamily" Value="./Resoures/Fonts/myFontFamily"/>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
</Style>
|
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml"/>
|
||||||
<conv:DotColorConverter x:Key="DotColorConverter"/>
|
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml"/>
|
||||||
<!--<conv:SpacingConverter x:Key="SpacingConverter"/>-->
|
</ResourceDictionary.MergedDictionaries>
|
||||||
</Application.Resources>
|
|
||||||
|
|
||||||
|
<!-- 2. 你的自定义资源 -->
|
||||||
|
<!-- 字体 -->
|
||||||
|
<FontFamily x:Key="AppFont">pack://application:,,,/YourApp;component/Resources/Fonts/#MyFontFamily</FontFamily>
|
||||||
|
|
||||||
|
<!-- 文本样式 -->
|
||||||
|
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
|
||||||
|
<Setter Property="FontFamily" Value="{StaticResource AppFont}"/>
|
||||||
|
<Setter Property="Foreground" Value="#333333"/>
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- 转换器 -->
|
||||||
|
<conv:DotColorConverter x:Key="DotColorConverter"/>
|
||||||
|
<!--<conv:SpacingConverter x:Key="SpacingConverter"/>-->
|
||||||
|
|
||||||
|
<!-- 其他自定义样式 -->
|
||||||
|
<Style x:Key="PrimaryButtonStyle" TargetType="Button" BasedOn="{StaticResource ButtonPrimary}">
|
||||||
|
<Setter Property="FontFamily" Value="{StaticResource AppFont}"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Application.Resources>
|
||||||
</Application>
|
</Application>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -106,12 +106,12 @@ public partial class MainWindow : Window
|
|||||||
|
|
||||||
private void ping_Click(object sender, RoutedEventArgs e)
|
private void ping_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var config = _context.BasicConfig.FirstOrDefault();
|
//var config = _context.BasicConfig.FirstOrDefault();
|
||||||
|
|
||||||
if (config != null)
|
//if (config != null)
|
||||||
{
|
//{
|
||||||
//var ip = config.Ip;
|
// //var ip = config.Ip;
|
||||||
}
|
//}
|
||||||
|
|
||||||
var host = this.host.Text.Trim();
|
var host = this.host.Text.Trim();
|
||||||
if (string.IsNullOrWhiteSpace(host))
|
if (string.IsNullOrWhiteSpace(host))
|
||||||
|
|||||||
@ -3,12 +3,12 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.MyUserControl"
|
xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.MyUserControl"
|
||||||
xmlns:gif="http://wpfanimatedgif.codeplex.com"
|
xmlns:gif="http://wpfanimatedgif.codeplex.com"
|
||||||
|
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||||
Height="1080" Width="1920" Loaded="Window_Loaded" Unloaded="Home_Unloaded">
|
Height="1080" Width="1920" Loaded="Window_Loaded" Unloaded="Home_Unloaded">
|
||||||
<Grid VerticalAlignment="Bottom">
|
<Grid VerticalAlignment="Bottom">
|
||||||
<Grid.Background>
|
<Grid.Background>
|
||||||
<ImageBrush ImageSource="/Resources/Img/Album/home_bg.png" Stretch="UniformToFill"/>
|
<ImageBrush ImageSource="/Resources/Img/Album/home_bg.png" Stretch="UniformToFill"/>
|
||||||
</Grid.Background>
|
</Grid.Background>
|
||||||
|
|
||||||
<!-- 顶部图片 -->
|
<!-- 顶部图片 -->
|
||||||
<Image
|
<Image
|
||||||
Source="/Resources/Img/Album/title.png"
|
Source="/Resources/Img/Album/title.png"
|
||||||
@ -74,5 +74,42 @@
|
|||||||
Width="880"
|
Width="880"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- 隐藏的管理员触发按钮(右上角)-->
|
||||||
|
<Button x:Name="HiddenAdminButton"
|
||||||
|
Width="60" Height="60"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
Margin="0,10,10,0"
|
||||||
|
Opacity="0"
|
||||||
|
Background="Transparent"
|
||||||
|
BorderThickness="0"
|
||||||
|
Click="HiddenAdminButton_Click"
|
||||||
|
Focusable="False"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 密码弹窗 -->
|
||||||
|
<Grid x:Name="AdminDialogGrid" Visibility="Hidden" Background="#80000000">
|
||||||
|
<Border CornerRadius="10" Width="420" Height="130" Background="#FFF">
|
||||||
|
<Grid HorizontalAlignment="Center" hc:BorderElement.CornerRadius="10" VerticalAlignment="Center" Width="420" Height="130" >
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Text="请输入管理员密码:" FontSize="16" Margin="12" Grid.Row="0"/>
|
||||||
|
<hc:PasswordBox x:Name="AdminPasswordBox" Grid.Row="1" Margin="12,4,12,0" FontSize="16" KeyDown="AdminPasswordBox_KeyDown"/>
|
||||||
|
|
||||||
|
|
||||||
|
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="12">
|
||||||
|
<RadioButton Style="{DynamicResource RadioGroupItemSingle}" Content="取消" x:Name="AdminCancelButton" Click="AdminCancelButton_Click" />
|
||||||
|
<hc:ButtonGroup Margin="30,0,0,0" Orientation="Vertical" Style="{StaticResource ButtonGroupSolid}">
|
||||||
|
<RadioButton Content="确定" IsChecked="True" x:Name="AdminOkButton" Click="AdminOkButton_Click"/>
|
||||||
|
</hc:ButtonGroup>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@ -14,6 +14,7 @@ using System.Windows.Controls;
|
|||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Animation;
|
using System.Windows.Media.Animation;
|
||||||
|
using System.Windows.Threading;
|
||||||
using Views.JumpLong;
|
using Views.JumpLong;
|
||||||
using Views.JumpRope;
|
using Views.JumpRope;
|
||||||
using Wpf_AiSportsMicrospace;
|
using Wpf_AiSportsMicrospace;
|
||||||
@ -39,13 +40,23 @@ namespace Wpf_AiSportsMicrospace
|
|||||||
{
|
{
|
||||||
public static Uri loadingImage = new Uri("/Resources/Img/Album/1.gif", UriKind.Relative);
|
public static Uri loadingImage = new Uri("/Resources/Img/Album/1.gif", UriKind.Relative);
|
||||||
public static Uri countDownGif = new Uri("/Resources/Img/gif/time_3.gif", UriKind.Relative);
|
public static Uri countDownGif = new Uri("/Resources/Img/gif/time_3.gif", UriKind.Relative);
|
||||||
|
private int _adminClickCount = 0;
|
||||||
|
private readonly DispatcherTimer _resetTimer;
|
||||||
private Main _mainWin => Application.Current.MainWindow as Main;
|
private Main _mainWin => Application.Current.MainWindow as Main;
|
||||||
|
|
||||||
public Home()
|
public Home()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
// 初始化隐藏按钮点击计数重置定时器(3 秒无操作则重置计数)
|
||||||
|
_resetTimer = new DispatcherTimer
|
||||||
|
{
|
||||||
|
Interval = TimeSpan.FromSeconds(3)
|
||||||
|
};
|
||||||
|
_resetTimer.Tick += (s, e) =>
|
||||||
|
{
|
||||||
|
_adminClickCount = 0;
|
||||||
|
_resetTimer.Stop();
|
||||||
|
};
|
||||||
string projectRoot = Path.Combine(AppContext.BaseDirectory, @"..\..\..");
|
string projectRoot = Path.Combine(AppContext.BaseDirectory, @"..\..\..");
|
||||||
string albumPath = Path.Combine(projectRoot, "Resources", "Img");
|
string albumPath = Path.Combine(projectRoot, "Resources", "Img");
|
||||||
|
|
||||||
@ -161,5 +172,77 @@ namespace Wpf_AiSportsMicrospace
|
|||||||
var mainWin = Application.Current.MainWindow as Main;
|
var mainWin = Application.Current.MainWindow as Main;
|
||||||
mainWin!.ShowCountDownAnimation();
|
mainWin!.ShowCountDownAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 隐藏按钮点击处理:连续点击五次显示内嵌密码弹窗
|
||||||
|
private void HiddenAdminButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_adminClickCount++;
|
||||||
|
// 每次点击重启计时器
|
||||||
|
_resetTimer.Stop();
|
||||||
|
_resetTimer.Start();
|
||||||
|
|
||||||
|
if (_adminClickCount >= 5)
|
||||||
|
{
|
||||||
|
_resetTimer.Stop();
|
||||||
|
_adminClickCount = 0;
|
||||||
|
|
||||||
|
// 显示内嵌密码弹窗
|
||||||
|
AdminPasswordBox.Password = string.Empty;
|
||||||
|
AdminDialogGrid.Visibility = Visibility.Visible;
|
||||||
|
AdminPasswordBox.Focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 密码弹窗:确定按钮
|
||||||
|
private void AdminOkButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var entered = AdminPasswordBox.Password ?? string.Empty;
|
||||||
|
if (entered == "8888")
|
||||||
|
{
|
||||||
|
AdminDialogGrid.Visibility = Visibility.Collapsed;
|
||||||
|
// 密码正确,打开管理员页面(已有 AdminWindow 或按需改为导航)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var mainWin = Application.Current.MainWindow as Main;
|
||||||
|
mainWin.SwitchPageWithMaskAnimation(new LongSttting(), true);
|
||||||
|
//var adminWin = new Views.AdminWindow();
|
||||||
|
//adminWin.Owner = Window.GetWindow(this);
|
||||||
|
//adminWin.Show();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show("密码正确");
|
||||||
|
|
||||||
|
Debug.WriteLine($"打开管理员页面异常: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MessageBox.Show("密码错误", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
AdminPasswordBox.Password = string.Empty;
|
||||||
|
AdminPasswordBox.Focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 密码弹窗:取消按钮
|
||||||
|
private void AdminCancelButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
AdminDialogGrid.Visibility = Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在 PasswordBox 按 Enter 等同于确定,按 Esc 等同于取消
|
||||||
|
private void AdminPasswordBox_KeyDown(object sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Key == Key.Enter)
|
||||||
|
{
|
||||||
|
AdminOkButton_Click(sender, e);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
else if (e.Key == Key.Escape)
|
||||||
|
{
|
||||||
|
AdminCancelButton_Click(sender, e);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
85
Wpf_AiSportsMicrospace/Views/Setting/LongSetting.xaml
Normal file
85
Wpf_AiSportsMicrospace/Views/Setting/LongSetting.xaml
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<UserControl x:Class="Wpf_AiSportsMicrospace.Views.LongSttting"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:local="clr-namespace:Wpf_AiSportsMicrospace.MyUserControl"
|
||||||
|
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||||
|
Loaded="Window_Loaded"
|
||||||
|
mc:Ignorable="d" Width="1920" Height="1080">
|
||||||
|
<Grid>
|
||||||
|
<Image x:Name="videoImage" Stretch="Uniform" />
|
||||||
|
<!-- 背景网格,用于显示坐标参考 -->
|
||||||
|
<Canvas x:Name="MainCanvas" >
|
||||||
|
<!-- 网格线 -->
|
||||||
|
<Canvas.Resources>
|
||||||
|
<DrawingBrush x:Key="GridBackground" Viewport="0,0,50,50" ViewportUnits="Absolute" TileMode="Tile">
|
||||||
|
<DrawingBrush.Drawing>
|
||||||
|
<DrawingGroup>
|
||||||
|
<GeometryDrawing Brush="White">
|
||||||
|
<GeometryDrawing.Geometry>
|
||||||
|
<RectangleGeometry Rect="0,0,50,50"/>
|
||||||
|
</GeometryDrawing.Geometry>
|
||||||
|
</GeometryDrawing>
|
||||||
|
<GeometryDrawing>
|
||||||
|
<GeometryDrawing.Pen>
|
||||||
|
<Pen Brush="LightGray" Thickness="0.5"/>
|
||||||
|
</GeometryDrawing.Pen>
|
||||||
|
<GeometryDrawing.Geometry>
|
||||||
|
<GeometryGroup>
|
||||||
|
<LineGeometry StartPoint="0,0" EndPoint="0,50"/>
|
||||||
|
<LineGeometry StartPoint="0,0" EndPoint="50,0"/>
|
||||||
|
</GeometryGroup>
|
||||||
|
</GeometryDrawing.Geometry>
|
||||||
|
</GeometryDrawing>
|
||||||
|
</DrawingGroup>
|
||||||
|
</DrawingBrush.Drawing>
|
||||||
|
</DrawingBrush>
|
||||||
|
</Canvas.Resources>
|
||||||
|
|
||||||
|
<!--<Canvas.Background>
|
||||||
|
<StaticResource ResourceKey="GridBackground"/>
|
||||||
|
</Canvas.Background>-->
|
||||||
|
|
||||||
|
<!-- 可拖动的点 -->
|
||||||
|
<Ellipse x:Name="TopPoint1" Width="20" Height="20" Fill="Red" Cursor="Hand"
|
||||||
|
Canvas.Left="245" Canvas.Top="741" Tag="TopPoint1"/>
|
||||||
|
<Ellipse x:Name="TopPoint2" Width="20" Height="20" Fill="Red" Cursor="Hand"
|
||||||
|
Canvas.Left="1498" Canvas.Top="730" Tag="TopPoint2"/>
|
||||||
|
<Ellipse x:Name="TopPoint3" Width="20" Height="20" Fill="Red" Cursor="Hand"
|
||||||
|
Canvas.Left="1689" Canvas.Top="724" Tag="TopPoint3"/>
|
||||||
|
|
||||||
|
<Ellipse x:Name="BottomPoint1" Width="20" Height="20" Fill="Blue" Cursor="Hand"
|
||||||
|
Canvas.Left="191" Canvas.Top="918" Tag="BottomPoint1"/>
|
||||||
|
<Ellipse x:Name="BottomPoint2" Width="20" Height="20" Fill="Blue" Cursor="Hand"
|
||||||
|
Canvas.Left="1516" Canvas.Top="918" Tag="BottomPoint2"/>
|
||||||
|
<Ellipse x:Name="BottomPoint3" Width="20" Height="20" Fill="Blue" Cursor="Hand"
|
||||||
|
Canvas.Left="1722" Canvas.Top="909" Tag="BottomPoint3"/>
|
||||||
|
|
||||||
|
<!-- 坐标显示标签 -->
|
||||||
|
<hc:Tag ShowCloseButton="False" IsSelected="True" x:Name="CoordinateDisplay" Content="你是"
|
||||||
|
Padding="18,0" FontSize="14" Visibility="Visible"/>
|
||||||
|
</Canvas>
|
||||||
|
|
||||||
|
<!-- 控制面板 -->
|
||||||
|
<StackPanel HorizontalAlignment="Right" VerticalAlignment="Top" Margin="20" Width="200">
|
||||||
|
<Border Background="#F0F0F0" Padding="15" CornerRadius="8">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="跳远点编辑器" FontSize="16" FontWeight="Bold" Margin="0,0,0,10"/>
|
||||||
|
|
||||||
|
<TextBlock Text="红色点:上边界点" Foreground="Red" FontWeight="Bold"/>
|
||||||
|
<TextBlock Text="蓝色点:下边界点" Foreground="Blue" FontWeight="Bold" Margin="0,0,0,10"/>
|
||||||
|
|
||||||
|
<Button x:Name="LoadPointsButton" Content="加载点数据" Click="LoadPointsButton_Click"
|
||||||
|
Margin="0,5" Height="30" Background="#4CAF50" Foreground="White"/>
|
||||||
|
<Button x:Name="SavePointsButton" Content="保存点数据" Click="SavePointsButton_Click"
|
||||||
|
Margin="0,5" Height="30" Background="#2196F3" Foreground="White"/>
|
||||||
|
<Button x:Name="ResetPointsButton" Content="重置为默认" Click="ResetPointsButton_Click"
|
||||||
|
Margin="0,5" Height="30" Background="#FF9800" Foreground="White"/>
|
||||||
|
|
||||||
|
<TextBlock x:Name="StatusText" Text="就绪" Margin="0,10,0,0" TextWrapping="Wrap"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
415
Wpf_AiSportsMicrospace/Views/Setting/LongSetting.xaml.cs
Normal file
415
Wpf_AiSportsMicrospace/Views/Setting/LongSetting.xaml.cs
Normal file
@ -0,0 +1,415 @@
|
|||||||
|
using AiSportsMicrospaceDB.DBContext;
|
||||||
|
using AiSportsMicrospaceDB.Entities;
|
||||||
|
using Emgu.CV.Reg;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using SharpDX.Direct3D9;
|
||||||
|
using SkiaSharp;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
using Wpf_AiSportsMicrospace.Common;
|
||||||
|
using Wpf_AiSportsMicrospace.Dto;
|
||||||
|
using Yztob.AiSports.Inferences.Abstractions;
|
||||||
|
using Yztob.AiSports.Inferences.Things;
|
||||||
|
using Yztob.AiSports.Postures.Sports;
|
||||||
|
using Yztob.AiSports.Postures.Things;
|
||||||
|
using Yztob.AiSports.Sensors.Abstractions;
|
||||||
|
using Yztob.AiSports.Sensors.Things;
|
||||||
|
|
||||||
|
namespace Wpf_AiSportsMicrospace.Views
|
||||||
|
{
|
||||||
|
public partial class LongSttting : UserControl
|
||||||
|
{
|
||||||
|
private bool _isDragging = false;
|
||||||
|
private Ellipse _draggedEllipse;
|
||||||
|
private System.Windows.Point _dragStartPoint;
|
||||||
|
private IHumanPredictor _humanPredictor;
|
||||||
|
private IObjectDetector _objectDetector;
|
||||||
|
private HumanGraphicsRenderer _humanGraphicsRenderer;
|
||||||
|
private readonly List<SportDescriptor> _sports;
|
||||||
|
private SportBase _sport;
|
||||||
|
private readonly SportDetectionQueue _detectQueue;
|
||||||
|
|
||||||
|
private WriteableBitmap _videoBitmap;
|
||||||
|
private int _lastFrameNumber = -1;
|
||||||
|
|
||||||
|
|
||||||
|
public LongSttting()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
SetupDragEvents();
|
||||||
|
LoadPointsFromDatabase();
|
||||||
|
var _webcamClient = WebcamClient.CreateRTSP("172.17.30.64", "admin", "yd708090", 554u);
|
||||||
|
|
||||||
|
//处理抽帧回调
|
||||||
|
_webcamClient.OnExtractFrame += this.OnFrameExtracted;
|
||||||
|
_webcamClient.StartExtract();//开始抽帧
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnFrameExtracted(VideoFrame frame)
|
||||||
|
{
|
||||||
|
if (frame == null) return;
|
||||||
|
|
||||||
|
// 跳帧:每秒只渲染 10~15 帧,降低 CPU 占用
|
||||||
|
if (_lastFrameNumber != -1 && frame.Number - _lastFrameNumber < 2) return;
|
||||||
|
_lastFrameNumber = (int)frame.Number;
|
||||||
|
|
||||||
|
//获得帧二进制流,保存图像、人体识别竺
|
||||||
|
var buffer = frame.GetImageBuffer(Yztob.AiSports.Sensors.Things.ImageFormat.Jpeg).ToArray();
|
||||||
|
//await File.WriteAllBytesAsync($"./temps/{frame.Number}.jpg", buffer);
|
||||||
|
|
||||||
|
// === 显示到 WPF ===
|
||||||
|
BitmapImage bitmap = null;
|
||||||
|
await Task.Run(() =>
|
||||||
|
{
|
||||||
|
using var ms = new MemoryStream(buffer);
|
||||||
|
bitmap = new BitmapImage();
|
||||||
|
bitmap.BeginInit();
|
||||||
|
bitmap.CacheOption = BitmapCacheOption.OnLoad;
|
||||||
|
bitmap.StreamSource = ms;
|
||||||
|
bitmap.EndInit();
|
||||||
|
bitmap.Freeze(); // 跨线程安全
|
||||||
|
});
|
||||||
|
|
||||||
|
// UI 线程显示
|
||||||
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
if (videoImage == null) return;
|
||||||
|
|
||||||
|
if (_videoBitmap == null ||
|
||||||
|
_videoBitmap.PixelWidth != bitmap.PixelWidth ||
|
||||||
|
_videoBitmap.PixelHeight != bitmap.PixelHeight)
|
||||||
|
{
|
||||||
|
_videoBitmap = new WriteableBitmap(bitmap.PixelWidth, bitmap.PixelHeight,
|
||||||
|
96, 96, PixelFormats.Bgra32, null);
|
||||||
|
videoImage.Source = _videoBitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
_videoBitmap.Lock();
|
||||||
|
bitmap.CopyPixels(new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight),
|
||||||
|
_videoBitmap.BackBuffer,
|
||||||
|
_videoBitmap.BackBufferStride * bitmap.PixelHeight,
|
||||||
|
_videoBitmap.BackBufferStride);
|
||||||
|
_videoBitmap.AddDirtyRect(new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight));
|
||||||
|
_videoBitmap.Unlock();
|
||||||
|
});
|
||||||
|
|
||||||
|
//var actualPositions = new List<(double X, double Y)>
|
||||||
|
//{
|
||||||
|
// (290, 510),
|
||||||
|
// (500, 700),
|
||||||
|
// (810, 710),
|
||||||
|
// (120, 880),
|
||||||
|
// (350, 880),
|
||||||
|
// (660, 880)
|
||||||
|
//};
|
||||||
|
|
||||||
|
////判断是否出圈
|
||||||
|
//if (configService.ConfigDic.TryGetValue("JumpRope", out var obj) && obj is ConfigSet cfg)
|
||||||
|
//{
|
||||||
|
// for (int i = 0; i < actualPositions.Count && i < cfg.Points.Count; i++)
|
||||||
|
// {
|
||||||
|
// var (ax, ay) = actualPositions[i];
|
||||||
|
// var pointCfg = cfg.Points[i];
|
||||||
|
|
||||||
|
// if (!IsInsideCircle(ax, ay, pointCfg))
|
||||||
|
// {
|
||||||
|
// Console.WriteLine($"⚠️ 学生{i + 1} 已经出圈!(坐标 {ax},{ay})");
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// Console.WriteLine($"✅ 学生{i + 1} 在圈内。");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//可以进一步进行人体识别等
|
||||||
|
//var humanResult = await Task.Run(() => _humanPredictor.Predicting(buffer, frame.Number));
|
||||||
|
|
||||||
|
//var human = humanResult?.Humans?.FirstOrDefault();
|
||||||
|
//_detectQueue.Enqueue(frame.Number, human, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupDragEvents()
|
||||||
|
{
|
||||||
|
// 为所有点添加鼠标事件
|
||||||
|
var points = new[] { TopPoint1, TopPoint2, TopPoint3, BottomPoint1, BottomPoint2, BottomPoint3 };
|
||||||
|
|
||||||
|
foreach (var point in points)
|
||||||
|
{
|
||||||
|
point.MouseLeftButtonDown += Point_MouseLeftButtonDown;
|
||||||
|
point.MouseMove += Point_MouseMove;
|
||||||
|
point.MouseLeftButtonUp += Point_MouseLeftButtonUp;
|
||||||
|
point.MouseEnter += Point_MouseEnter;
|
||||||
|
point.MouseLeave += Point_MouseLeave;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void LoadPointsFromDatabase()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var app = (App)Application.Current;
|
||||||
|
using var scope = App.AppHost.Services.CreateScope();
|
||||||
|
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
var pointsData = await db.JumpLongPoints.FirstOrDefaultAsync();
|
||||||
|
if (pointsData != null)
|
||||||
|
{
|
||||||
|
// 加载 TopPoint1
|
||||||
|
var topPoint1 = pointsData.TopPoint1;
|
||||||
|
Canvas.SetLeft(TopPoint1, topPoint1.X - 10); // 减去半径以居中
|
||||||
|
Canvas.SetTop(TopPoint1, topPoint1.Y - 10);
|
||||||
|
|
||||||
|
// 加载 TopPoint2
|
||||||
|
var topPoint2 = pointsData.TopPoint2;
|
||||||
|
Canvas.SetLeft(TopPoint2, topPoint2.X - 10);
|
||||||
|
Canvas.SetTop(TopPoint2, topPoint2.Y - 10);
|
||||||
|
|
||||||
|
// 加载 TopPoint3
|
||||||
|
var topPoint3 = pointsData.TopPoint3;
|
||||||
|
Canvas.SetLeft(TopPoint3, topPoint3.X - 10);
|
||||||
|
Canvas.SetTop(TopPoint3, topPoint3.Y - 10);
|
||||||
|
|
||||||
|
// 加载 BottomPoint1
|
||||||
|
var bottomPoint1 = pointsData.BottomPoint1;
|
||||||
|
Canvas.SetLeft(BottomPoint1, bottomPoint1.X - 10);
|
||||||
|
Canvas.SetTop(BottomPoint1, bottomPoint1.Y - 10);
|
||||||
|
|
||||||
|
// 加载 BottomPoint2
|
||||||
|
var bottomPoint2 = pointsData.BottomPoint2;
|
||||||
|
Canvas.SetLeft(BottomPoint2, bottomPoint2.X - 10);
|
||||||
|
Canvas.SetTop(BottomPoint2, bottomPoint2.Y - 10);
|
||||||
|
|
||||||
|
// 加载 BottomPoint3
|
||||||
|
var bottomPoint3 = pointsData.BottomPoint3;
|
||||||
|
Canvas.SetLeft(BottomPoint3, bottomPoint3.X - 10);
|
||||||
|
Canvas.SetTop(BottomPoint3, bottomPoint3.Y - 10);
|
||||||
|
|
||||||
|
UpdateStatus("点数据加载成功");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
UpdateStatus($"加载点数据失败: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//保存点数据到数据库
|
||||||
|
private void SavePointsToDatabase()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var app = (App)Application.Current;
|
||||||
|
using var scope = App.AppHost.Services.CreateScope();
|
||||||
|
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
var pointsData = db.JumpLongPoints.FirstOrDefault();
|
||||||
|
if (pointsData == null)
|
||||||
|
{
|
||||||
|
pointsData = new JumpLongPoints { Id = 1 };
|
||||||
|
db.JumpLongPoints.Add(pointsData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存所有点的位置
|
||||||
|
pointsData.TopPoint1 = new AiSportsMicrospaceDB.Entities.Point
|
||||||
|
{
|
||||||
|
X = Canvas.GetLeft(TopPoint1) + 10,
|
||||||
|
Y = Canvas.GetTop(TopPoint1) + 10
|
||||||
|
};
|
||||||
|
|
||||||
|
pointsData.TopPoint2 = new AiSportsMicrospaceDB.Entities.Point
|
||||||
|
{
|
||||||
|
X = Canvas.GetLeft(TopPoint2) + 10,
|
||||||
|
Y = Canvas.GetTop(TopPoint2) + 10
|
||||||
|
};
|
||||||
|
|
||||||
|
pointsData.TopPoint3 = new AiSportsMicrospaceDB.Entities.Point
|
||||||
|
{
|
||||||
|
X = Canvas.GetLeft(TopPoint3) + 10,
|
||||||
|
Y = Canvas.GetTop(TopPoint3) + 10
|
||||||
|
};
|
||||||
|
|
||||||
|
pointsData.BottomPoint1 = new AiSportsMicrospaceDB.Entities.Point
|
||||||
|
{
|
||||||
|
X = Canvas.GetLeft(BottomPoint1) + 10,
|
||||||
|
Y = Canvas.GetTop(BottomPoint1) + 10
|
||||||
|
};
|
||||||
|
|
||||||
|
pointsData.BottomPoint2 = new AiSportsMicrospaceDB.Entities.Point
|
||||||
|
{
|
||||||
|
X = Canvas.GetLeft(BottomPoint2) + 10,
|
||||||
|
Y = Canvas.GetTop(BottomPoint2) + 10
|
||||||
|
};
|
||||||
|
|
||||||
|
pointsData.BottomPoint3 = new AiSportsMicrospaceDB.Entities.Point
|
||||||
|
{
|
||||||
|
X = Canvas.GetLeft(BottomPoint3) + 10,
|
||||||
|
Y = Canvas.GetTop(BottomPoint3) + 10
|
||||||
|
};
|
||||||
|
|
||||||
|
db.SaveChanges();
|
||||||
|
UpdateStatus("点数据保存成功");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
UpdateStatus($"保存点数据失败: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#region 鼠标事件处理
|
||||||
|
|
||||||
|
private void Point_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
_isDragging = true;
|
||||||
|
_draggedEllipse = sender as Ellipse;
|
||||||
|
_dragStartPoint = e.GetPosition(MainCanvas);
|
||||||
|
_draggedEllipse.CaptureMouse();
|
||||||
|
|
||||||
|
// 提升点的Z索引,使其在拖动时显示在最前面
|
||||||
|
Canvas.SetZIndex(_draggedEllipse, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Point_MouseMove(object sender, MouseEventArgs e)
|
||||||
|
{
|
||||||
|
if (_isDragging && _draggedEllipse != null)
|
||||||
|
{
|
||||||
|
var currentPosition = e.GetPosition(MainCanvas);
|
||||||
|
|
||||||
|
// 计算新的位置
|
||||||
|
double newX = currentPosition.X - 10; // 减去半径
|
||||||
|
double newY = currentPosition.Y - 10;
|
||||||
|
|
||||||
|
// 限制在Canvas范围内
|
||||||
|
newX = Math.Max(0, Math.Min(newX, MainCanvas.ActualWidth - 20));
|
||||||
|
newY = Math.Max(0, Math.Min(newY, MainCanvas.ActualHeight - 20));
|
||||||
|
|
||||||
|
Canvas.SetLeft(_draggedEllipse, newX);
|
||||||
|
Canvas.SetTop(_draggedEllipse, newY);
|
||||||
|
|
||||||
|
// 更新坐标显示
|
||||||
|
UpdateCoordinateDisplay(currentPosition, _draggedEllipse.Tag.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Point_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
if (_isDragging)
|
||||||
|
{
|
||||||
|
_isDragging = false;
|
||||||
|
_draggedEllipse.ReleaseMouseCapture();
|
||||||
|
Canvas.SetZIndex(_draggedEllipse, 0); // 恢复Z索引
|
||||||
|
_draggedEllipse = null;
|
||||||
|
|
||||||
|
// 隐藏坐标显示
|
||||||
|
CoordinateDisplay.Visibility = Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Point_MouseEnter(object sender, MouseEventArgs e)
|
||||||
|
{
|
||||||
|
var ellipse = sender as Ellipse;
|
||||||
|
ellipse.Opacity = 0.8;
|
||||||
|
|
||||||
|
// 显示坐标提示
|
||||||
|
var position = e.GetPosition(MainCanvas);
|
||||||
|
UpdateCoordinateDisplay(position, ellipse.Tag.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Point_MouseLeave(object sender, MouseEventArgs e)
|
||||||
|
{
|
||||||
|
var ellipse = sender as Ellipse;
|
||||||
|
ellipse.Opacity = 1.0;
|
||||||
|
|
||||||
|
if (!_isDragging)
|
||||||
|
{
|
||||||
|
CoordinateDisplay.Visibility = Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateCoordinateDisplay(System.Windows.Point position, string pointName)
|
||||||
|
{
|
||||||
|
CoordinateDisplay.Content = $"{pointName}: ({position.X:F0}, {position.Y:F0})";
|
||||||
|
Canvas.SetLeft(CoordinateDisplay, position.X + 10);
|
||||||
|
Canvas.SetTop(CoordinateDisplay, position.Y + 10);
|
||||||
|
CoordinateDisplay.Visibility = Visibility.Visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 按钮事件处理
|
||||||
|
|
||||||
|
private void LoadPointsButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
LoadPointsFromDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SavePointsButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
SavePointsToDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetPointsButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var result = MessageBox.Show("确定要重置为默认点位置吗?", "确认重置",
|
||||||
|
MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||||||
|
|
||||||
|
if (result == MessageBoxResult.Yes)
|
||||||
|
{
|
||||||
|
CreateDefaultPoints();
|
||||||
|
LoadPointsFromDatabase();
|
||||||
|
UpdateStatus("已重置为默认点位置");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private void UpdateStatus(string message)
|
||||||
|
{
|
||||||
|
StatusText.Text = $"{DateTime.Now:HH:mm:ss} - {message}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void CreateDefaultPoints()
|
||||||
|
{
|
||||||
|
var app = (App)Application.Current;
|
||||||
|
using var scope = App.AppHost.Services.CreateScope();
|
||||||
|
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
var pointsData = db.JumpLongPoints.FirstOrDefault();
|
||||||
|
if (pointsData == null)
|
||||||
|
{
|
||||||
|
pointsData = new JumpLongPoints { Id = 1 };
|
||||||
|
db.JumpLongPoints.Add(pointsData);
|
||||||
|
}
|
||||||
|
var defaultPoints = new JumpLongPoints
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
TopPoint1 = new AiSportsMicrospaceDB.Entities.Point { X = 245, Y = 741 },
|
||||||
|
TopPoint2 = new AiSportsMicrospaceDB.Entities.Point { X = 1498, Y = 730 },
|
||||||
|
TopPoint3 = new AiSportsMicrospaceDB.Entities.Point { X = 1689, Y = 724 },
|
||||||
|
BottomPoint1 = new AiSportsMicrospaceDB.Entities.Point { X = 191, Y = 918 },
|
||||||
|
BottomPoint2 = new AiSportsMicrospaceDB.Entities.Point { X = 1516, Y = 918 },
|
||||||
|
BottomPoint3 = new AiSportsMicrospaceDB.Entities.Point { X = 1722, Y = 909 },
|
||||||
|
};
|
||||||
|
pointsData = defaultPoints;
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user