Implementadas nuevas funcionalidades en el menú contextual, incluyendo opciones para abrir y limpiar el archivo de log, así como una ventana "Acerca de" con información sobre la aplicación. Se mejoró la gestión de recursos de PaddleOCR al cerrar la aplicación.

This commit is contained in:
Miguel 2025-06-16 22:19:14 +02:00
parent bd55330739
commit 6d8f70d15b
6 changed files with 282 additions and 47 deletions

View File

@ -215,6 +215,10 @@ namespace GTPCorrgir
{
_cancellationTokenSource?.Cancel();
_cancellationTokenSource?.Dispose();
// Limpiar recursos de PaddleOCR
PaddleOCRManager.Cleanup();
base.OnExit(e);
}

View File

@ -1,11 +1,14 @@
<Window x:Class="GTPCorrgir.ContextMenuWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:GTPCorrgir" Title="Menu"
WindowStyle="None" AllowsTransparency="True" Background="Transparent" ShowInTaskbar="False" Topmost="True"
SizeToContent="WidthAndHeight" KeyDown="Window_KeyDown" Deactivated="Window_Deactivated">
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:GTPCorrgir"
xmlns:av="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="av"
x:Class="GTPCorrgir.ContextMenuWindow" Title="Menu" WindowStyle="None" AllowsTransparency="True"
Background="Transparent" ShowInTaskbar="False" Topmost="True" SizeToContent="WidthAndHeight"
KeyDown="Window_KeyDown" Deactivated="Window_Deactivated" av:DesignHeight="700">
<Window.Resources>
<local:NullToBooleanConverter x:Key="NullToBooleanConverter" />
<Style x:Key="CloseButtonStyle" TargetType="Button">
<Style x:Key="CloseButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="#757575" />
<Setter Property="BorderThickness" Value="0" />
@ -13,7 +16,7 @@
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ControlTemplate TargetType="{x:Type Button}">
<Grid Background="Transparent">
<Border x:Name="border" Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2"
@ -38,6 +41,27 @@
</Window.Resources>
<Border Background="#F5F5F5" BorderBrush="#CCCCCC" BorderThickness="1" CornerRadius="3" Margin="5">
<Border.ContextMenu>
<ContextMenu>
<MenuItem Header="Abrir Log" Click="OpenLog_Click">
<MenuItem.Icon>
<TextBlock Text="📄" FontSize="14" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Limpiar Log" Click="ClearLog_Click">
<MenuItem.Icon>
<TextBlock Text="🗑️" FontSize="14" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Acerca de" Click="About_Click">
<MenuItem.Icon>
<TextBlock Text="" FontSize="14" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Border.ContextMenu>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@ -60,12 +84,12 @@
<!-- Contenido principal -->
<Grid Grid.Row="1" Margin="10,0,10,10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox x:Name="ModeListBox" Height="200" Grid.Row="0" Margin="0,0,0,10">
<ListBox x:Name="ModeListBox" Grid.Row="0" Margin="0,0,0,10">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" Padding="5,3" />
@ -75,7 +99,7 @@
<StackPanel Grid.Row="1" Margin="0,0,0,10">
<TextBlock Text="Modelo LLM:" FontWeight="Bold" Margin="0,0,0,5" />
<ListBox x:Name="LLMListBox" Height="120">
<ListBox x:Name="LLMListBox" Height="205">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" Padding="5,3" />
@ -84,8 +108,8 @@
</ListBox>
</StackPanel>
<Button Grid.Row="2" Content="Aceptar" Height="30" Width="80" HorizontalAlignment="Right"
Click="AcceptButton_Click" Margin="0,10,0,0">
<Button Grid.Row="2" Content="Aceptar" Height="30" Width="80" HorizontalAlignment="Center"
Click="AcceptButton_Click" Margin="0,0,0,0">
<!-- ... resto del estilo del botón ... -->
</Button>
</Grid>

View File

@ -5,6 +5,9 @@ using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Data;
using System.Windows.Controls;
using System.IO;
using System.Diagnostics;
using KeyEventArgs = System.Windows.Input.KeyEventArgs;
using MessageBox = System.Windows.MessageBox;
@ -155,6 +158,7 @@ namespace GTPCorrgir
UpdateOptionsAndClose(selectedMode, selectedLLM);
}
}
private void UpdateOptionsAndClose(MenuOption selectedMode, MenuOption selectedLLM)
{
optionsSelected = true;
@ -170,5 +174,198 @@ namespace GTPCorrgir
// Eliminar DialogResult
this.Close();
}
// Manejadores del menú contextual
private void OpenLog_Click(object sender, RoutedEventArgs e)
{
try
{
string logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logfile.log");
if (File.Exists(logFilePath))
{
// Intentar abrir con el programa predeterminado
Process.Start(new ProcessStartInfo
{
FileName = logFilePath,
UseShellExecute = true
});
}
else
{
MessageBox.Show("El archivo de log no existe aún.", "Información",
MessageBoxButton.OK, MessageBoxImage.Information);
}
}
catch (Exception ex)
{
MessageBox.Show($"Error al abrir el archivo de log: {ex.Message}", "Error",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void ClearLog_Click(object sender, RoutedEventArgs e)
{
try
{
var result = MessageBox.Show(
"¿Está seguro de que desea limpiar el archivo de log?\nEsta acción no se puede deshacer.",
"Confirmar limpieza de log",
MessageBoxButton.YesNo,
MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
{
string logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logfile.log");
if (File.Exists(logFilePath))
{
try
{
// Obtener todos los listeners de tipo TextWriterTraceListener
var textWriterListeners = new List<System.Diagnostics.TextWriterTraceListener>();
foreach (System.Diagnostics.TraceListener listener in System.Diagnostics.Trace.Listeners)
{
if (listener is System.Diagnostics.TextWriterTraceListener textListener)
{
textWriterListeners.Add(textListener);
}
}
// Cerrar y remover temporalmente los listeners
foreach (var listener in textWriterListeners)
{
listener.Flush();
listener.Close();
System.Diagnostics.Trace.Listeners.Remove(listener);
}
// Esperar un momento para asegurar que el archivo se libere
System.Threading.Thread.Sleep(100);
// Limpiar el archivo
File.WriteAllText(logFilePath, string.Empty);
// Recrear los listeners
foreach (var listener in textWriterListeners)
{
var newListener = new System.Diagnostics.TextWriterTraceListener(logFilePath);
System.Diagnostics.Trace.Listeners.Add(newListener);
}
// Reactivar AutoFlush
System.Diagnostics.Trace.AutoFlush = true;
MessageBox.Show("El archivo de log ha sido limpiado correctamente.", "Éxito",
MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (UnauthorizedAccessException)
{
MessageBox.Show(
"No se puede acceder al archivo de log. Puede que esté siendo usado por otro proceso.\n" +
"Intente cerrar todas las instancias de la aplicación y vuelva a intentarlo.",
"Acceso denegado",
MessageBoxButton.OK, MessageBoxImage.Warning);
}
catch (IOException ioEx)
{
MessageBox.Show(
$"Error de E/S al acceder al archivo de log:\n{ioEx.Message}\n\n" +
"El archivo puede estar siendo usado por otro proceso.",
"Error de archivo",
MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
else
{
MessageBox.Show("El archivo de log no existe.", "Información",
MessageBoxButton.OK, MessageBoxImage.Information);
}
}
}
catch (Exception ex)
{
MessageBox.Show($"Error al limpiar el archivo de log: {ex.Message}", "Error",
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void About_Click(object sender, RoutedEventArgs e)
{
var aboutDialog = new Window
{
Title = "Acerca de GTPCorregir",
Width = 400,
Height = 300,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Owner = this,
ResizeMode = ResizeMode.NoResize,
ShowInTaskbar = false,
WindowStyle = WindowStyle.ToolWindow
};
var stackPanel = new StackPanel { Margin = new Thickness(20) };
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
{
Text = "GTPCorregir",
FontSize = 18,
FontWeight = FontWeights.Bold,
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
Margin = new Thickness(0, 0, 0, 10)
});
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
{
Text = "Herramienta de corrección de texto con IA",
FontSize = 12,
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
Margin = new Thickness(0, 0, 0, 15)
});
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
{
Text = "• Corrección de ortografía y gramática",
Margin = new Thickness(0, 2, 0, 2)
});
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
{
Text = "• Traducción a múltiples idiomas",
Margin = new Thickness(0, 2, 0, 2)
});
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
{
Text = "• OCR con PaddleOCR",
Margin = new Thickness(0, 2, 0, 2)
});
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
{
Text = "• Soporte para múltiples LLMs",
Margin = new Thickness(0, 2, 0, 2)
});
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
{
Text = $"Versión: {System.Reflection.Assembly.GetExecutingAssembly().GetName().Version}",
FontSize = 10,
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
Margin = new Thickness(0, 20, 0, 0)
});
var closeButton = new System.Windows.Controls.Button
{
Content = "Cerrar",
Width = 80,
Height = 25,
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
Margin = new Thickness(0, 15, 0, 0)
};
closeButton.Click += (s, args) => aboutDialog.Close();
stackPanel.Children.Add(closeButton);
aboutDialog.Content = stackPanel;
aboutDialog.ShowDialog();
}
}
}

View File

@ -31,8 +31,6 @@
<PackageReference Include="Paddle.Runtime.win_x64" Version="3.0.0" />
<PackageReference Include="PaddleOCRSharp" Version="5.0.0.1" />
<PackageReference Include="System.Drawing.Common" Version="9.0.1" />
<PackageReference Include="Tesseract" Version="5.2.0" />
<PackageReference Include="Tesseract.Drawing" Version="5.2.0" />
</ItemGroup>
<ItemGroup>
@ -45,15 +43,6 @@
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="tessdata\eng.traineddata">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="tessdata\ita.traineddata">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="tessdata\spa.traineddata">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -6,8 +6,9 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Controls;
using System.Windows.Shapes;
using Tesseract;
using PaddleOCRSharp;
using System.Threading.Tasks;
using System.Linq;
using Point = System.Windows.Point;
using Rect = System.Windows.Rect;
using Size = System.Windows.Size;
@ -24,6 +25,7 @@ using System.Windows.Data;
using Binding = System.Windows.Data.Binding;
using Image = System.Windows.Controls.Image;
namespace GTPCorrgir
{
public class ScreenCaptureWindow : Window
@ -35,7 +37,7 @@ namespace GTPCorrgir
private Canvas overlayCanvas;
private readonly Cursor crosshairCursor = Cursors.Cross;
private bool isSelecting;
private TextBlock dimensionsText;
private System.Windows.Controls.TextBlock dimensionsText;
public ScreenCaptureWindow()
{
@ -284,7 +286,7 @@ namespace GTPCorrgir
var screenCapture = CaptureScreen(x, y, width, height);
if (screenCapture == null) return;
// Resto del procesamiento OCR igual que antes
// Procesamiento OCR con PaddleOCR
using (var memoryStream = new MemoryStream())
{
var encoder = new PngBitmapEncoder();
@ -292,14 +294,37 @@ namespace GTPCorrgir
encoder.Save(memoryStream);
memoryStream.Position = 0;
string tesseractPath = GetTesseractExecutablePath();
using (var engine = new TesseractEngine(tesseractPath, "eng", EngineMode.Default))
try
{
using (var img = Pix.LoadFromMemory(memoryStream.ToArray()))
{
var result = engine.Process(img);
string ocrText = result.GetText();
// Convertir a array de bytes
byte[] imageBytes = memoryStream.ToArray();
// Obtener el motor PaddleOCR
var ocrEngine = PaddleOCRManager.GetEngine();
// Usar PaddleOCR para detectar texto
OCRResult result = ocrEngine.DetectText(imageBytes);
string ocrText = "";
if (result != null && result.TextBlocks != null && result.TextBlocks.Count > 0)
{
// Lista de caracteres permitidos (similar a la whitelist de Tesseract)
string allowedChars = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-./";
// Filtrar texto por lista de caracteres permitidos
var filteredBlocks = result.TextBlocks
.Where(block => block.Score > 0.5) // Solo bloques con confianza > 50%
.Select(block => new {
Text = new string(block.Text.Where(c => allowedChars.Contains(c)).ToArray()),
Score = block.Score
})
.OrderByDescending(block => block.Score);
ocrText = string.Join(" ", filteredBlocks.Select(b => b.Text));
}
if (!string.IsNullOrEmpty(ocrText))
{
using (var textProcessor = new OcrTextProcessor())
{
string processedText = await textProcessor.ProcessOcrText(ocrText);
@ -308,6 +333,17 @@ namespace GTPCorrgir
notificationWindow.Show();
}
}
else
{
var notificationWindow = new notificacion();
notificationWindow.UpdateNotification("Sin texto", "No se pudo detectar texto en la imagen seleccionada");
notificationWindow.Show();
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Error en OCR: {ex.Message}");
MessageBox.Show($"Error en OCR: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
@ -329,20 +365,5 @@ namespace GTPCorrgir
this.Close();
}
}
private string GetTesseractExecutablePath()
{
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
string tessDataDir = Path.Combine(baseDir, "tessdata");
if (!Directory.Exists(tessDataDir))
{
throw new DirectoryNotFoundException(
$"No se encontró el directorio tessdata en {tessDataDir}. " +
"Asegúrate de que la carpeta tessdata existe y contiene los archivos de entrenamiento.");
}
return tessDataDir;
}
}
}

View File

@ -7,7 +7,7 @@ using System.Windows;
using System.Linq;
using PaddleOCRSharp;
namespace CtrEditor.Services
namespace GTPCorrgir
{
// Clase auxiliar para gestionar PaddleOCR
public static class PaddleOCRManager