Skip to content

Commit 4e5df8a

Browse files
committed
Interfaz y clases para administrar acciones de bot.
1 parent 02a3bbd commit 4e5df8a

File tree

5 files changed

+389
-0
lines changed

5 files changed

+389
-0
lines changed

main.py

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import pathlib
2+
import pygubu
3+
from rutina.accion import Accion
4+
from utils.helpers import select_area
5+
from tkinter import Toplevel, Label, Entry, Button
6+
7+
PROJECT_PATH = pathlib.Path(__file__).parent
8+
PROJECT_UI = PROJECT_PATH / "main.ui"
9+
10+
class IziBot:
11+
def __init__(self, master=None):
12+
self.builder = builder = pygubu.Builder()
13+
builder.add_resource_path(PROJECT_PATH)
14+
builder.add_from_file(PROJECT_UI)
15+
self.mainwindow = builder.get_object('main', master)
16+
builder.connect_callbacks(self)
17+
18+
# Obtener referencia al Treeview de Rutina
19+
self.treeview_rutina = self.builder.get_object('tvRutina')
20+
# Configurar columnas del Treeview
21+
self.treeview_rutina['columns'] = ('#1', '#2')
22+
self.treeview_rutina.heading('#0', text='ID')
23+
self.treeview_rutina.column('#0', width=50)
24+
self.treeview_rutina.heading('#1', text='Acción')
25+
self.treeview_rutina.heading('#2', text='Cordenadas/Texto/Tiempo')
26+
# Lista que contiene las acciones de la rutina (simulación)
27+
self.listaAccionesUsuario = []
28+
29+
# Obtener referencia de boton Clic
30+
self.btnAddClic = self.builder.get_object('btnClic')
31+
32+
# Variable para guardar area seleccionada
33+
self.temp_area = None
34+
# Variable para guardar texto leído
35+
self.texto_leido = None
36+
37+
def run(self):
38+
self.mainwindow.mainloop()
39+
40+
def agregar_accion(self, tipo_accion, *args):
41+
accion = Accion(tipo_accion, *args)
42+
43+
if len(args) == 1:
44+
args = args[0] # Si solo hay un valor en args, lo desempaquetamos
45+
46+
self.listaAccionesUsuario.append(accion)
47+
48+
# Insertar nueva acción en el Treeview
49+
idx = len(self.listaAccionesUsuario)
50+
self.treeview_rutina.insert('', 'end', text=str(idx), values=(tipo_accion, str(args)))
51+
52+
def add_clic(self):
53+
self.temp_area = select_area(self.mainwindow)
54+
x1, y1, x2, y2 = self.temp_area
55+
self.agregar_accion("clicEnPantalla", x1, y1)
56+
57+
def add_esperar(self):
58+
# Crear un cuadro de diálogo para ingresar el tiempo de espera
59+
self.dialog = Toplevel(self.mainwindow)
60+
self.dialog.title("Ingrese el tiempo de espera")
61+
62+
Label(self.dialog, text="Ingrese el tiempo en segundos:").pack(pady=10)
63+
64+
# Entrada para el tiempo
65+
self.entry_tiempo = Entry(self.dialog)
66+
self.entry_tiempo.pack(pady=5)
67+
68+
# Botón para confirmar la entrada
69+
Button(self.dialog, text="Aceptar", command=self.confirmar_espera).pack(pady=10)
70+
71+
def confirmar_espera(self):
72+
# Obtener el valor ingresado
73+
tiempo = self.entry_tiempo.get()
74+
75+
# Verificar que el valor sea un número válido
76+
try:
77+
tiempo = int(tiempo)
78+
if tiempo < 0:
79+
raise ValueError("El tiempo no puede ser negativo.")
80+
except ValueError:
81+
print("Por favor, ingrese un número válido.")
82+
return
83+
84+
# Cerrar el cuadro de diálogo
85+
self.dialog.destroy()
86+
87+
# Agregar la acción de esperar con el tiempo ingresado
88+
self.agregar_accion("esperar", tiempo)
89+
90+
def add_escribir(self):
91+
# Crear ventana emergente para que el usuario ingrese el texto
92+
self.dialog = Toplevel(self.mainwindow)
93+
self.dialog.title("Ingresar texto para escribir")
94+
95+
# Etiqueta
96+
label = Label(self.dialog, text="Texto a escribir:")
97+
label.pack(padx=10, pady=10)
98+
99+
# Campo de entrada de texto
100+
self.entry_texto = Entry(self.dialog)
101+
self.entry_texto.pack(padx=10, pady=10)
102+
103+
# Botón para confirmar
104+
boton_confirmar = Button(self.dialog, text="Confirmar", command=self.confirmar_escribir)
105+
boton_confirmar.pack(padx=10, pady=10)
106+
107+
def confirmar_escribir(self):
108+
# Obtener el texto ingresado
109+
texto = self.entry_texto.get()
110+
111+
# Verificar que se haya ingresado algún texto
112+
if not texto:
113+
print("Por favor, ingrese un texto válido.")
114+
return
115+
116+
# Cerrar la ventana emergente
117+
self.dialog.destroy()
118+
119+
# Agregar la acción de 'escribir' con el texto ingresado
120+
self.agregar_accion("escribir", texto)
121+
122+
def add_leer(self):
123+
self.temp_area = select_area(self.mainwindow)
124+
self.agregar_accion("leerTextoEnPantalla", self.temp_area)
125+
126+
def iniciar_rutina(self):
127+
for accion in self.listaAccionesUsuario:
128+
accion.ejecutar()
129+
130+
def eliminar_accion(self):
131+
# Obtener el elemento seleccionado en el Treeview
132+
selected_item = self.treeview_rutina.selection()
133+
134+
if selected_item:
135+
# Obtener el índice del elemento seleccionado
136+
item_index = int(self.treeview_rutina.item(selected_item, "text")) - 1 # Restar 1 porque el índice es 1-based
137+
138+
# Eliminar el elemento de listaAccionesUsuario
139+
if 0 <= item_index < len(self.listaAccionesUsuario):
140+
del self.listaAccionesUsuario[item_index]
141+
142+
# Eliminar el elemento del Treeview
143+
self.treeview_rutina.delete(selected_item)
144+
145+
# Actualizar el Treeview para reflejar los nuevos índices y argumentos correctamente
146+
self.actualizar_treeview()
147+
else:
148+
print("No se ha seleccionado ninguna acción.")
149+
150+
def actualizar_treeview(self):
151+
# Eliminar todos los elementos del Treeview
152+
for item in self.treeview_rutina.get_children():
153+
self.treeview_rutina.delete(item)
154+
155+
# Volver a agregar todos los elementos de listaAccionesUsuario
156+
for idx, accion in enumerate(self.listaAccionesUsuario, start=1):
157+
# Si los argumentos tienen un solo elemento, lo mostramos sin paréntesis
158+
args = accion.args if len(accion.args) > 1 else accion.args[0] if accion.args else ''
159+
160+
# Insertar de nuevo en el Treeview
161+
self.treeview_rutina.insert('', 'end', text=str(idx), values=(accion.tipo_accion, str(args)))
162+
163+
if __name__ == "__main__":
164+
app = IziBot()
165+
app.run()

main.ui

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<?xml version='1.0' encoding='utf-8'?>
2+
<interface version="1.4" author="PygubuDesigner 0.39.3">
3+
<project>
4+
<settings />
5+
<customwidgets />
6+
</project>
7+
<object class="tk.Tk" id="main">
8+
<property name="height">200</property>
9+
<property name="title" translatable="yes">IziBot</property>
10+
<child>
11+
<object class="ttk.Treeview" id="tvRutina" named="True">
12+
<property name="height">12</property>
13+
<property name="selectmode">extended</property>
14+
<layout manager="grid">
15+
<property name="column">0</property>
16+
<property name="padx">6</property>
17+
<property name="pady">6</property>
18+
<property name="row">0</property>
19+
<property name="sticky">ne</property>
20+
</layout>
21+
</object>
22+
</child>
23+
<child>
24+
<object class="ttk.Labelframe" id="labelframe1">
25+
<property name="height">200</property>
26+
<property name="text" translatable="yes">Controles</property>
27+
<property name="width">200</property>
28+
<layout manager="grid">
29+
<property name="column">1</property>
30+
<property name="padx">6</property>
31+
<property name="pady">3</property>
32+
<property name="row">0</property>
33+
<property name="sticky">nw</property>
34+
</layout>
35+
<child>
36+
<object class="ttk.Button" id="btnClic" named="True">
37+
<property name="command" type="command" cbtype="simple">add_clic</property>
38+
<property name="text" translatable="yes">Clic</property>
39+
<layout manager="pack">
40+
<property name="ipadx">6</property>
41+
<property name="padx">6</property>
42+
<property name="pady">6</property>
43+
</layout>
44+
</object>
45+
</child>
46+
<child>
47+
<object class="ttk.Button" id="btnEsperar" named="True">
48+
<property name="command" type="command" cbtype="simple">add_esperar</property>
49+
<property name="text" translatable="yes">Esperar</property>
50+
<layout manager="pack">
51+
<property name="ipadx">6</property>
52+
<property name="padx">6</property>
53+
<property name="pady">6</property>
54+
<property name="side">top</property>
55+
</layout>
56+
</object>
57+
</child>
58+
<child>
59+
<object class="ttk.Button" id="btnEscribir" named="True">
60+
<property name="command" type="command" cbtype="simple">add_escribir</property>
61+
<property name="text" translatable="yes">Escribir</property>
62+
<layout manager="pack">
63+
<property name="ipadx">6</property>
64+
<property name="padx">6</property>
65+
<property name="pady">6</property>
66+
<property name="side">top</property>
67+
</layout>
68+
</object>
69+
</child>
70+
<child>
71+
<object class="ttk.Button" id="btnLeer" named="True">
72+
<property name="command" type="command" cbtype="simple">add_leer</property>
73+
<property name="text" translatable="yes">Leer</property>
74+
<layout manager="pack">
75+
<property name="ipadx">6</property>
76+
<property name="padx">6</property>
77+
<property name="pady">6</property>
78+
<property name="side">top</property>
79+
</layout>
80+
</object>
81+
</child>
82+
<child>
83+
<object class="ttk.Button" id="btnQuitar" named="True">
84+
<property name="command" type="command" cbtype="simple">eliminar_accion</property>
85+
<property name="text" translatable="yes">Quitar</property>
86+
<layout manager="pack">
87+
<property name="ipadx">6</property>
88+
<property name="padx">6</property>
89+
<property name="pady">6</property>
90+
<property name="side">top</property>
91+
</layout>
92+
</object>
93+
</child>
94+
<child>
95+
<object class="ttk.Checkbutton" id="checkBtn" named="True">
96+
<property name="text" translatable="yes">Bucle</property>
97+
<layout manager="pack">
98+
<property name="fill">both</property>
99+
<property name="padx">6</property>
100+
<property name="side">top</property>
101+
</layout>
102+
</object>
103+
</child>
104+
<child>
105+
<object class="ttk.Button" id="btnIniciar" named="True">
106+
<property name="command" type="command" cbtype="simple">iniciar_rutina</property>
107+
<property name="text" translatable="yes">Iniciar</property>
108+
<layout manager="pack">
109+
<property name="ipadx">6</property>
110+
<property name="padx">6</property>
111+
<property name="pady">6</property>
112+
<property name="side">top</property>
113+
</layout>
114+
</object>
115+
</child>
116+
</object>
117+
</child>
118+
</object>
119+
</interface>

rutina/accion.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from core import clicEnPantalla, leerTextoEnPantalla, esperar, escribir
2+
#from core import clickEnImagen
3+
from utils.log import log_error
4+
5+
class Accion:
6+
def __init__(self, tipo_accion, *args):
7+
self.tipo_accion = tipo_accion # Ejemplo: "clicEnPantalla"
8+
self.args = args # Argumentos necesarios para la acción (x, y, etc.)
9+
10+
def ejecutar(self):
11+
# Diccionario de acciones posibles con sus funciones respectivas
12+
acciones = {
13+
'clicEnPantalla': clicEnPantalla,
14+
'leerTextoEnPantalla': leerTextoEnPantalla,
15+
'esperar': esperar,
16+
#'buscarImagenEnPantalla': clickEnImagen,
17+
'escribir': escribir
18+
}
19+
20+
# Ejecuta la acción llamando a la función correspondiente
21+
if self.tipo_accion in acciones:
22+
acciones[self.tipo_accion](*self.args)
23+
else:
24+
log_error(f"Acción {self.tipo_accion} no reconocida.")

rutina/rutina.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from rutina.accion import Accion
2+
from utils.log import log_info
3+
4+
class Rutina:
5+
def __init__(self):
6+
self.listaAccionesUsuario = [] # Lista que almacena las acciones
7+
8+
def agregar_accion(self, tipo_accion, *args):
9+
accion = Accion(tipo_accion, *args)
10+
self.listaAccionesUsuario.append(accion)
11+
log_info(f"Acción '{tipo_accion}' agregada a la rutina con argumentos {args}")
12+
13+
def ejecutar_rutina(self):
14+
log_info("Ejecutando rutina...")
15+
for accion in self.listaAccionesUsuario:
16+
accion.ejecutar()
17+
log_info("Rutina finalizada.")

utils/helpers.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from tkinter import Toplevel, Canvas
2+
import pyautogui
3+
4+
def select_area(mainwindow):
5+
# Oculta la ventana principal durante la selección de área
6+
mainwindow.withdraw()
7+
8+
# Crear una nueva ventana para seleccionar el área
9+
area_selector_window = Toplevel(mainwindow)
10+
area_selector_window.title("Select Area")
11+
area_selector_window.attributes('-alpha', 0.3) # Transparencia
12+
area_selector_window.attributes('-topmost', True) # Ventana siempre en primer plano
13+
area_selector_window.overrideredirect(True) # Sin bordes
14+
screen_width, screen_height = pyautogui.size()
15+
area_selector_window.geometry(f"{screen_width}x{screen_height}+0+0")
16+
17+
# Crear un Canvas para la selección del área
18+
canvas = Canvas(area_selector_window, cursor="cross", bg="black", highlightthickness=0)
19+
canvas.pack(fill="both", expand=True)
20+
21+
# Diccionario para almacenar las coordenadas
22+
coords = {'start_x': 0, 'start_y': 0, 'end_x': 0, 'end_y': 0, 'search_area': None}
23+
24+
def on_button_press(event):
25+
coords['start_x'] = event.x
26+
coords['start_y'] = event.y
27+
coords['rect'] = canvas.create_rectangle(
28+
coords['start_x'], coords['start_y'],
29+
coords['start_x'], coords['start_y'],
30+
outline='red'
31+
)
32+
33+
def on_mouse_drag(event):
34+
canvas.coords(coords['rect'], coords['start_x'], coords['start_y'], event.x, event.y)
35+
36+
def on_button_release(event):
37+
# Guardar las coordenadas finales
38+
coords['end_x'] = event.x
39+
coords['end_y'] = event.y
40+
41+
# Asegúrate de que x1 < x2 y y1 < y2
42+
x1 = min(coords['start_x'], coords['end_x'])
43+
y1 = min(coords['start_y'], coords['end_y'])
44+
x2 = max(coords['start_x'], coords['end_x'])
45+
y2 = max(coords['start_y'], coords['end_y'])
46+
47+
# Guarda las coordenadas absolutas
48+
coords['search_area'] = (x1, y1, x2, y2)
49+
area_selector_window.destroy()
50+
51+
# Mostrar mensaje en la consola
52+
# print(f"Área seleccionada: {coords['search_area']}")
53+
54+
# Vincular los eventos al Canvas
55+
canvas.bind("<ButtonPress-1>", on_button_press)
56+
canvas.bind("<B1-Motion>", on_mouse_drag)
57+
canvas.bind("<ButtonRelease-1>", on_button_release)
58+
59+
# Esperar a que se cierre la ventana de selección
60+
mainwindow.wait_window(area_selector_window)
61+
mainwindow.deiconify()
62+
63+
# Retornar el área seleccionada
64+
return coords['search_area']

0 commit comments

Comments
 (0)