feat: 初始提交——一键运行快捷轮盘工具(WPF + C#)
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
# 验证轮盘中心文本居中:标题像素包围盒应关于画布中心 (268,268) 对称,且状态间完全一致
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
|
||||
function Measure-Hub($path, [string]$mode) {
|
||||
$bmp = [System.Drawing.Bitmap]::FromFile((Resolve-Path $path))
|
||||
$cx = 268; $cy = 268; $hubR = 54
|
||||
$darkN = 0; $darkX = 0.0; $darkY = 0.0
|
||||
$darkMinX = 9999; $darkMaxX = -1; $darkMinY = 9999; $darkMaxY = -1
|
||||
$grayN = 0; $grayX = 0.0; $grayY = 0.0
|
||||
for ($y = $cy - $hubR; $y -le $cy + $hubR; $y++) {
|
||||
for ($x = $cx - $hubR; $x -le $cx + $hubR; $x++) {
|
||||
$dx = $x - $cx; $dy = $y - $cy
|
||||
if ($dx * $dx + $dy * $dy -gt $hubR * $hubR) { continue }
|
||||
$px = $bmp.GetPixel($x, $y)
|
||||
if ($px.A -lt 200) { continue }
|
||||
$sum = $px.R + $px.G + $px.B
|
||||
if ($mode -eq 'light') {
|
||||
if ($sum -lt 330) {
|
||||
$darkN++; $darkX += $x; $darkY += $y
|
||||
if ($x -lt $darkMinX) { $darkMinX = $x }; if ($x -gt $darkMaxX) { $darkMaxX = $x }
|
||||
if ($y -lt $darkMinY) { $darkMinY = $y }; if ($y -gt $darkMaxY) { $darkMaxY = $y }
|
||||
}
|
||||
elseif ($sum -ge 300 -and $sum -le 420) { $grayN++; $grayX += $x; $grayY += $y }
|
||||
} else {
|
||||
if ($sum -gt 560) {
|
||||
$darkN++; $darkX += $x; $darkY += $y
|
||||
if ($x -lt $darkMinX) { $darkMinX = $x }; if ($x -gt $darkMaxX) { $darkMaxX = $x }
|
||||
if ($y -lt $darkMinY) { $darkMinY = $y }; if ($y -gt $darkMaxY) { $darkMaxY = $y }
|
||||
}
|
||||
elseif ($sum -ge 430 -and $sum -le 560) { $grayN++; $grayX += $x; $grayY += $y }
|
||||
}
|
||||
}
|
||||
}
|
||||
$bmp.Dispose()
|
||||
$bboxCX = if ($darkN -gt 0) { ($darkMinX + $darkMaxX) / 2.0 } else { -1 }
|
||||
Write-Output (" TitlePixelCount={0} BboxX=[{1},{2}] BboxCenterX={3} (期望268) BboxY=[{4},{5}]" -f $darkN, $darkMinX, $darkMaxX, [Math]::Round($bboxCX, 2), $darkMinY, $darkMaxY)
|
||||
Write-Output (" HintPixelCount={0} HintCenterX={1} (供参考)" -f $grayN, $(if ($grayN -gt 0) { [Math]::Round($grayX / $grayN, 2) } else { -1 }))
|
||||
}
|
||||
|
||||
foreach ($file in 'wheel-light.png', 'wheel-light-hover.png') {
|
||||
Write-Output "=== $file ==="
|
||||
Measure-Hub "artifacts\$file" 'light'
|
||||
}
|
||||
foreach ($file in 'wheel-dark.png', 'wheel-dark-hover.png') {
|
||||
Write-Output "=== $file ==="
|
||||
Measure-Hub "artifacts\$file" 'dark'
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
# 验证扇形文字走向:水平文字 → 白色水平笔画段占优;旧径向布局 → 对角笔画段占优
|
||||
# 并校验图标位置:新布局图标在文字上方(旧布局图标朝外侧/下方)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
$bmp = [System.Drawing.Bitmap]::FromFile((Resolve-Path 'artifacts\wheel-light.png'))
|
||||
|
||||
# 3 项配置:扇形中点 -30°/90°/210°,中半径 145
|
||||
$regions = @(
|
||||
@{ Name = '扇形0(右上, 旧布局=对角60°)'; X = 328; Y = 168; W = 132; H = 57 },
|
||||
@{ Name = '扇形1(下方, 旧布局=倒置水平)'; X = 204; Y = 385; W = 128; H = 55 },
|
||||
@{ Name = '扇形2(左侧, 旧布局=对角-60°)'; X = 77; Y = 168; W = 132; H = 57 }
|
||||
)
|
||||
|
||||
function Is-White($px) { return $px.A -gt 200 -and ($px.R + $px.G + $px.B) -gt 700 }
|
||||
|
||||
foreach ($r in $regions) {
|
||||
$x0 = $r.X; $y0 = $r.Y; $w = $r.W; $h = $r.H
|
||||
$white = New-Object 'bool[,]' ($h + 2), ($w + 2)
|
||||
$topN = 0; $midN = 0; $botN = 0
|
||||
for ($y = 0; $y -lt $h; $y++) {
|
||||
for ($x = 0; $x -lt $w; $x++) {
|
||||
$isWhite = Is-White ($bmp.GetPixel($x0 + $x, $y0 + $y))
|
||||
$white[$y, $x] = $isWhite
|
||||
if ($isWhite) {
|
||||
if ($y -lt [int]($h / 3)) { $topN++ }
|
||||
elseif ($y -lt [int](2 * $h / 3)) { $midN++ }
|
||||
else { $botN++ }
|
||||
}
|
||||
}
|
||||
}
|
||||
$hRuns = 0; $vRuns = 0; $d1Runs = 0; $d2Runs = 0
|
||||
for ($y = 0; $y -lt $h; $y++) {
|
||||
$len = 0
|
||||
for ($x = 0; $x -le $w; $x++) {
|
||||
if ($x -lt $w -and $white[$y, $x]) { $len++ }
|
||||
else { if ($len -ge 4) { $hRuns++ }; $len = 0 }
|
||||
}
|
||||
}
|
||||
for ($x = 0; $x -lt $w; $x++) {
|
||||
$len = 0
|
||||
for ($y = 0; $y -le $h; $y++) {
|
||||
if ($y -lt $h -and $white[$y, $x]) { $len++ }
|
||||
else { if ($len -ge 4) { $vRuns++ }; $len = 0 }
|
||||
}
|
||||
}
|
||||
for ($s = 0; $s -lt ($w + $h - 1); $s++) {
|
||||
$len = 0
|
||||
for ($y = 0; $y -lt $h; $y++) {
|
||||
$x = $s - $y
|
||||
if ($x -ge 0 -and $x -lt $w) {
|
||||
if ($white[$y, $x]) { $len++ } else { if ($len -ge 4) { $d1Runs++ }; $len = 0 }
|
||||
}
|
||||
}
|
||||
if ($len -ge 4) { $d1Runs++ }
|
||||
}
|
||||
for ($s = -($h - 1); $s -lt $w; $s++) {
|
||||
$len = 0
|
||||
for ($x = 0; $x -lt $w; $x++) {
|
||||
$y = $x - $s
|
||||
if ($y -ge 0 -and $y -lt $h) {
|
||||
if ($white[$y, $x]) { $len++ } else { if ($len -ge 4) { $d2Runs++ }; $len = 0 }
|
||||
}
|
||||
}
|
||||
if ($len -ge 4) { $d2Runs++ }
|
||||
}
|
||||
Write-Output ("{0}" -f $r.Name)
|
||||
Write-Output (" 笔画段: 水平={0} 垂直={1} 对角45={2} 对角-45={3} (水平文字期望: 水平占优, 对角极少)" -f $hRuns, $vRuns, $d1Runs, $d2Runs)
|
||||
Write-Output (" 白色像素分布 上/中/下 = {0}/{1}/{2} (新布局期望: 上段为图标密集, 下段为文字)" -f $topN, $midN, $botN)
|
||||
}
|
||||
$bmp.Dispose()
|
||||
@@ -0,0 +1,41 @@
|
||||
# 像素级验证轮盘扇形间隙宽度(任意半径处应恒定 ≈ 10px)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
|
||||
foreach ($name in 'wheel-light', 'wheel-dark') {
|
||||
$path = "artifacts\$name.png"
|
||||
$bmp = [System.Drawing.Bitmap]::FromFile((Resolve-Path $path))
|
||||
$cx = [int]($bmp.Width / 2); $cy = [int]($bmp.Height / 2)
|
||||
Write-Output "=== $name ($($bmp.Width)x$($bmp.Height)) ==="
|
||||
|
||||
foreach ($radius in 100, 145, 200) {
|
||||
$steps = 7200
|
||||
$transparentRuns = New-Object System.Collections.Generic.List[double]
|
||||
$opaqueRuns = New-Object System.Collections.Generic.List[double]
|
||||
$runSteps = 0; $prevTransparent = $false
|
||||
for ($i = 0; $i -le $steps; $i++) {
|
||||
$ang = ($i / $steps) * 2 * [Math]::PI
|
||||
$x = [int][Math]::Round($cx + $radius * [Math]::Cos($ang))
|
||||
$y = [int][Math]::Round($cy + $radius * [Math]::Sin($ang))
|
||||
$px = $bmp.GetPixel($x, $y)
|
||||
$transparent = $px.A -lt 30
|
||||
if ($i -gt 0 -and $transparent -ne $prevTransparent) {
|
||||
$pxArc = $runSteps / $steps * 2 * [Math]::PI * $radius
|
||||
if ($prevTransparent) { $transparentRuns.Add($pxArc) } else { $opaqueRuns.Add($pxArc) }
|
||||
$runSteps = 0
|
||||
}
|
||||
$runSteps++; $prevTransparent = $transparent
|
||||
}
|
||||
$gaps = @($transparentRuns | Where-Object { $_ -gt 1 -and $_ -lt 40 })
|
||||
$sectors = @($opaqueRuns | Where-Object { $_ -gt 40 })
|
||||
$gapAvg = -1; $gapMin = -1; $gapMax = -1
|
||||
if ($gaps.Count -gt 0) {
|
||||
$gapAvg = ($gaps | Measure-Object -Average).Average
|
||||
$gapMin = ($gaps | Measure-Object -Minimum).Minimum
|
||||
$gapMax = ($gaps | Measure-Object -Maximum).Maximum
|
||||
}
|
||||
$line = '半径 {0}: 扇形段数={1}, 间隙段数={2}, 间隙宽度 均值={3:F2}px 最小={4:F2}px 最大={5:F2}px (期望≈10px 且处处相等)' -f $radius, $sectors.Count, $gaps.Count, $gapAvg, $gapMin, $gapMax
|
||||
Write-Output $line
|
||||
}
|
||||
$bmp.Dispose()
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
# 单实例验证:第二个实例应在 2 秒内自动退出
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$exe = 'E:\002_Demo\One_click_run_wpf\src\OneClickRun\bin\Debug\net8.0-windows\OneClickRun.exe'
|
||||
Get-Process -Name 'OneClickRun' -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.Path -eq $exe } |
|
||||
ForEach-Object { Stop-Process -Id $_.Id -Force }
|
||||
|
||||
$first = Start-Process -FilePath $exe -PassThru
|
||||
Start-Sleep -Seconds 3
|
||||
$second = Start-Process -FilePath $exe -PassThru
|
||||
Start-Sleep -Seconds 2
|
||||
$second.Refresh()
|
||||
Write-Output "FirstAlive: $(-not $first.HasExited)"
|
||||
Write-Output "SecondExited: $($second.HasExited)"
|
||||
if (-not $second.HasExited) { Stop-Process -Id $second.Id -Force }
|
||||
Stop-Process -Id $first.Id -Force
|
||||
Write-Output 'Done'
|
||||
@@ -0,0 +1,46 @@
|
||||
# 一键运行 - 启动冒烟验证脚本(PowerShell 7.6.5)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$exe = 'E:\002_Demo\One_click_run_wpf\src\OneClickRun\bin\Debug\net8.0-windows\OneClickRun.exe'
|
||||
if (-not (Test-Path $exe)) { throw "未找到构建产物: $exe" }
|
||||
|
||||
# 清理上次冒烟残留(仅本路径实例)
|
||||
Get-Process -Name 'OneClickRun' -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.Path -eq $exe } |
|
||||
ForEach-Object { Stop-Process -Id $_.Id -Force }
|
||||
|
||||
$logFile = Join-Path $env:APPDATA 'OneClickRun\logs\app-{0}.log' -f (Get-Date -Format 'yyyyMMdd')
|
||||
$logBefore = 0
|
||||
if (Test-Path $logFile) { $logBefore = (Get-Item $logFile).Length }
|
||||
|
||||
$p = Start-Process -FilePath $exe -PassThru
|
||||
Start-Sleep -Seconds 6
|
||||
$p.Refresh()
|
||||
$alive = -not $p.HasExited
|
||||
Write-Output "ProcessAlive: $alive"
|
||||
|
||||
$cfg = Join-Path $env:APPDATA 'OneClickRun\config.json'
|
||||
Write-Output "ConfigExists: $(Test-Path $cfg)"
|
||||
if (Test-Path $cfg) {
|
||||
$json = Get-Content $cfg -Raw | ConvertFrom-Json
|
||||
Write-Output "ConfigItems: $($json.items.Count)"
|
||||
Write-Output "HotkeyText: $($json.general.hotkey.modifiers -join '+')+$($json.general.hotkey.key)"
|
||||
Write-Output "TriggerMode: $($json.general.triggerMode)"
|
||||
Write-Output "SelectionMode: $($json.general.selectionMode)"
|
||||
Write-Output "Theme: $($json.general.theme)"
|
||||
}
|
||||
|
||||
Write-Output '--- 本次启动新增日志 ---'
|
||||
if (Test-Path $logFile) {
|
||||
$stream = [System.IO.File]::Open($logFile, 'Open', 'Read', 'ReadWrite')
|
||||
$reader = New-Object System.IO.StreamReader($stream)
|
||||
$reader.BaseStream.Seek($logBefore, 'Begin') | Out-Null
|
||||
$delta = $reader.ReadToEnd()
|
||||
$reader.Close(); $stream.Close()
|
||||
Write-Output $delta
|
||||
}
|
||||
|
||||
if ($alive) {
|
||||
Stop-Process -Id $p.Id -Force
|
||||
Write-Output 'ProcessStopped: true'
|
||||
}
|
||||
Write-Output 'SmokeDone'
|
||||
Reference in New Issue
Block a user