mirror of
https://github.com/fishyboteso/fishyboteso.git
synced 2024-08-30 18:32:13 +00:00
Merge pull request #155 from fishyboteso/wip/screenshot_lib_selector
screenshot library selected
This commit is contained in:
commit
70af635025
@ -5,8 +5,10 @@ chalutier = ("Chalutier", "https://www.esoui.com/downloads/dl2934/Chalutier_1.1.
|
||||
|
||||
# addons used
|
||||
lam2 = ("LibAddonMenu-2.0", "https://cdn.esoui.com/downloads/file7/LibAddonMenu-2.0r34.zip", 34)
|
||||
fishyqr = ("FishyQR", "https://github.com/fishyboteso/FishyQR/releases/download/v1.5/FishyQR.zip", 150)
|
||||
fishyqr = ("FishyQR", "https://github.com/fishyboteso/FishyQR/releases/download/v1.6/FishyQR.zip", 151)
|
||||
libgps = ("LibGPS", "https://cdn.esoui.com/downloads/file601/LibGPS_3_2_0.zip", 32)
|
||||
libmapping = ("LibMapPing", "https://cdn.esoui.com/downloads/file1302/LibMapPing_2_0_0.zip", 200)
|
||||
libdl = ("LibDebugLogger", "https://cdn.esoui.com/downloads/file2275/LibDebugLogger_2_4_1.zip", 241)
|
||||
libchatmsg = ("LibChatMessage", "https://cdn.esoui.com/downloads/file2382/LibChatMessage_1_2_0.zip", 120)
|
||||
|
||||
d3dshot_git = "git+https://github.com/fauskanger/D3DShot.git#egg=D3DShot"
|
||||
|
@ -65,7 +65,7 @@ class IEngine:
|
||||
# noinspection PyBroadException
|
||||
def _crash_safe(self):
|
||||
logging.debug(f"starting {self.name} engine thread")
|
||||
self.window = WindowClient(color=cv2.COLOR_RGB2GRAY, show_name=f"{self.name} debug")
|
||||
self.window = WindowClient()
|
||||
self.gui.bot_started(True)
|
||||
try:
|
||||
self.run()
|
||||
|
104
fishy/engine/common/screenshot.py
Normal file
104
fishy/engine/common/screenshot.py
Normal file
@ -0,0 +1,104 @@
|
||||
import logging
|
||||
import subprocess
|
||||
from abc import ABC, abstractmethod
|
||||
from functools import partial
|
||||
from typing import Optional
|
||||
|
||||
import numpy as np
|
||||
from numpy import ndarray
|
||||
|
||||
from fishy import constants
|
||||
from fishy.helper.config import config
|
||||
from fishy.osservices.os_services import os_services
|
||||
|
||||
|
||||
class IScreenShot(ABC):
|
||||
@abstractmethod
|
||||
def setup(self) -> bool:
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def grab(self) -> ndarray:
|
||||
...
|
||||
|
||||
|
||||
def get_monitor_id(monitors_iterator, get_top_left) -> Optional[int]:
|
||||
monitor_rect = os_services.get_monitor_rect()
|
||||
if monitor_rect is None:
|
||||
logging.error("Game window not found")
|
||||
return None
|
||||
|
||||
for i, m in enumerate(monitors_iterator):
|
||||
top, left = get_top_left(m)
|
||||
if top == monitor_rect[1] and left == monitor_rect[0]:
|
||||
return i
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class MSS(IScreenShot):
|
||||
def __init__(self):
|
||||
from mss import mss
|
||||
self.monitor_id = None
|
||||
self.sct = mss()
|
||||
|
||||
def setup(self) -> bool:
|
||||
self.monitor_id = get_monitor_id(self.sct.monitors, lambda m: (m["top"], m["left"]))
|
||||
return self.monitor_id is not None
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
def grab(self) -> ndarray:
|
||||
sct_img = self.sct.grab(self.sct.monitors[self.monitor_id])
|
||||
return np.array(sct_img)
|
||||
|
||||
|
||||
class PyAutoGUI(IScreenShot):
|
||||
def __init__(self):
|
||||
self.monitor_rect = None
|
||||
|
||||
def setup(self) -> bool:
|
||||
from PIL import ImageGrab
|
||||
ImageGrab.grab = partial(ImageGrab.grab, all_screens=True)
|
||||
self.monitor_rect = os_services.get_monitor_rect()
|
||||
return True
|
||||
|
||||
def grab(self) -> ndarray:
|
||||
import pyautogui
|
||||
image = pyautogui.screenshot()
|
||||
img = np.array(image)
|
||||
crop = self.monitor_rect
|
||||
img = img[crop[1]:crop[3], crop[0]:crop[2]]
|
||||
return img
|
||||
|
||||
|
||||
class D3DShot(IScreenShot):
|
||||
# noinspection PyPackageRequirements
|
||||
def __init__(self):
|
||||
try:
|
||||
import d3dshot
|
||||
except ImportError:
|
||||
logging.info("Installing d3dshot please wait...")
|
||||
subprocess.call(["python", "-m", "pip", "install", constants.d3dshot_git])
|
||||
import d3dshot
|
||||
|
||||
self.d3 = d3dshot.create(capture_output="numpy")
|
||||
|
||||
def setup(self) -> bool:
|
||||
monitor_id = get_monitor_id(self.d3.displays, lambda m: (m.position["top"], m.position["left"]))
|
||||
if monitor_id is None:
|
||||
return False
|
||||
|
||||
self.d3.display = self.d3.displays[monitor_id]
|
||||
return True
|
||||
|
||||
def grab(self) -> ndarray:
|
||||
return self.d3.screenshot()
|
||||
|
||||
|
||||
LIBS = [MSS, PyAutoGUI, D3DShot]
|
||||
|
||||
|
||||
def create() -> IScreenShot:
|
||||
lib = LIBS[config.get("sslib", 0)]
|
||||
logging.debug(f"Using {lib.__name__} screenshot lib")
|
||||
return lib()
|
@ -1,4 +1,5 @@
|
||||
import logging
|
||||
import uuid
|
||||
from typing import List
|
||||
|
||||
import cv2
|
||||
@ -7,26 +8,52 @@ import imutils
|
||||
from fishy.engine.common import window_server
|
||||
from fishy.engine.common.window_server import Status, WindowServer
|
||||
from fishy.helper import helper
|
||||
from fishy.helper.config import config
|
||||
|
||||
|
||||
class WindowClient:
|
||||
clients: List['WindowClient'] = []
|
||||
|
||||
def __init__(self, crop=None, color=None, scale=None, show_name=None):
|
||||
def __init__(self):
|
||||
"""
|
||||
create a window instance with these pre process
|
||||
:param crop: [x1,y1,x2,y2] array defining the boundaries to crop
|
||||
:param color: color to use example cv2.COLOR_RGB2HSV
|
||||
:param scale: scaling the window
|
||||
"""
|
||||
self.color = color
|
||||
self.crop = crop
|
||||
self.scale = scale
|
||||
self.show_name = show_name
|
||||
self.crop = None
|
||||
self.scale = None
|
||||
self.show_name = f"window client {len(WindowClient.clients)}"
|
||||
|
||||
if len(WindowClient.clients) == 0:
|
||||
window_server.start()
|
||||
WindowClient.clients.append(self)
|
||||
if len(WindowClient.clients) > 0 and WindowServer.status != Status.RUNNING:
|
||||
window_server.start()
|
||||
|
||||
@staticmethod
|
||||
def running():
|
||||
return WindowServer.status == Status.RUNNING
|
||||
|
||||
def processed_image(self, func=None):
|
||||
"""
|
||||
processes the image using the function provided
|
||||
:param func: function to process image
|
||||
:return: processed image
|
||||
"""
|
||||
if WindowServer.status == Status.CRASHED:
|
||||
return None
|
||||
|
||||
img = self._get_capture()
|
||||
|
||||
if img is None:
|
||||
return None
|
||||
|
||||
if func:
|
||||
img = func(img)
|
||||
|
||||
if config.get("show_grab", 0):
|
||||
self._show(img)
|
||||
|
||||
return img
|
||||
|
||||
def destroy(self):
|
||||
if self in WindowClient.clients:
|
||||
@ -34,11 +61,7 @@ class WindowClient:
|
||||
if len(WindowClient.clients) == 0:
|
||||
window_server.stop()
|
||||
|
||||
@staticmethod
|
||||
def running():
|
||||
return WindowServer.status == Status.RUNNING
|
||||
|
||||
def get_capture(self):
|
||||
def _get_capture(self):
|
||||
"""
|
||||
copies the recorded screen and then pre processes its
|
||||
:return: game window image
|
||||
@ -56,8 +79,7 @@ class WindowClient:
|
||||
if temp_img is None or temp_img.size == 0:
|
||||
return None
|
||||
|
||||
if self.color is not None:
|
||||
temp_img = cv2.cvtColor(temp_img, self.color)
|
||||
temp_img = cv2.cvtColor(temp_img, cv2.COLOR_RGB2GRAY)
|
||||
|
||||
if self.crop is not None:
|
||||
temp_img = temp_img[self.crop[1]:self.crop[3], self.crop[0]:self.crop[2]]
|
||||
@ -71,49 +93,12 @@ class WindowClient:
|
||||
|
||||
return temp_img
|
||||
|
||||
def processed_image(self, func=None):
|
||||
"""
|
||||
processes the image using the function provided
|
||||
:param func: function to process image
|
||||
:return: processed image
|
||||
"""
|
||||
if WindowServer.status == Status.CRASHED:
|
||||
return None
|
||||
|
||||
img = self.get_capture()
|
||||
|
||||
if img is None:
|
||||
return None
|
||||
|
||||
if func is None:
|
||||
return img
|
||||
|
||||
return func(img)
|
||||
|
||||
def show(self, to_show, resize=None, func=None):
|
||||
# noinspection PyUnresolvedReferences
|
||||
def _show(self, img):
|
||||
"""
|
||||
Displays the processed image for debugging purposes
|
||||
:param to_show: false to destroy the window
|
||||
:param resize: scale the image to make small images more visible
|
||||
:param func: function to process the image
|
||||
"""
|
||||
if WindowServer.status == Status.CRASHED:
|
||||
return
|
||||
|
||||
if not self.show_name:
|
||||
logging.warning("You need to assign a name first")
|
||||
return
|
||||
|
||||
if not to_show:
|
||||
cv2.destroyWindow(self.show_name)
|
||||
return
|
||||
|
||||
img = self.processed_image(func)
|
||||
|
||||
if img is None:
|
||||
return
|
||||
|
||||
if resize is not None:
|
||||
img = imutils.resize(img, width=resize)
|
||||
cv2.imshow(self.show_name, img)
|
||||
cv2.waitKey(25)
|
||||
helper.save_img(self.show_name, img)
|
||||
|
@ -2,11 +2,13 @@ import logging
|
||||
from enum import Enum
|
||||
from threading import Thread
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
from mss import mss
|
||||
from mss.base import MSSBase
|
||||
|
||||
from fishy.engine.common import screenshot
|
||||
from fishy.helper import helper
|
||||
from fishy.helper.config import config
|
||||
from fishy.helper.helper import print_exc
|
||||
from fishy.osservices.os_services import os_services
|
||||
|
||||
@ -24,10 +26,8 @@ class WindowServer:
|
||||
Screen: np.ndarray = None
|
||||
windowOffset = None
|
||||
status = Status.STOPPED
|
||||
sct: MSSBase = None
|
||||
|
||||
sslib = None
|
||||
crop = None
|
||||
monitor_id = -1
|
||||
|
||||
|
||||
def init():
|
||||
@ -35,30 +35,31 @@ def init():
|
||||
Executed once before the main loop,
|
||||
Finds the game window, and calculates the offset to remove the title bar
|
||||
"""
|
||||
WindowServer.sslib = screenshot.create()
|
||||
WindowServer.status = Status.RUNNING
|
||||
WindowServer.sct = mss()
|
||||
|
||||
WindowServer.crop = os_services.get_game_window_rect()
|
||||
monitor_rect = os_services.get_monitor_rect()
|
||||
|
||||
if monitor_rect is None or WindowServer.crop is None:
|
||||
logging.error("Game window not found")
|
||||
if WindowServer.crop is None or not WindowServer.sslib.setup():
|
||||
logging.error("Game window not found by window_server")
|
||||
WindowServer.status = Status.CRASHED
|
||||
return
|
||||
|
||||
for i, m in enumerate(WindowServer.sct.monitors):
|
||||
if m["top"] == monitor_rect[0] and m["left"] == monitor_rect[1]:
|
||||
WindowServer.monitor_id = i
|
||||
|
||||
|
||||
def get_cropped_screenshot():
|
||||
sct_img = WindowServer.sct.grab(WindowServer.sct.monitors[WindowServer.monitor_id])
|
||||
# noinspection PyTypeChecker
|
||||
ss = np.array(sct_img)
|
||||
ss = WindowServer.sslib.grab()
|
||||
|
||||
if config.get("show_grab", 0):
|
||||
helper.save_img("full screen", ss)
|
||||
|
||||
crop = WindowServer.crop
|
||||
cropped_ss = ss[crop[1]:crop[3], crop[0]:crop[2]]
|
||||
|
||||
if cropped_ss.size == 0:
|
||||
return None
|
||||
|
||||
if config.get("show_grab", 0):
|
||||
helper.save_img("Game window", cropped_ss)
|
||||
|
||||
return cropped_ss
|
||||
|
||||
|
||||
|
@ -79,12 +79,6 @@ class FullAuto(IEngine):
|
||||
logging.error("exception occurred while running full auto mode")
|
||||
print_exc()
|
||||
|
||||
def start_show(self):
|
||||
def func():
|
||||
while self.start and WindowClient.running():
|
||||
self.window.show(self.show_crop)
|
||||
Thread(target=func).start()
|
||||
|
||||
def stop_on_inactive(self):
|
||||
def func():
|
||||
logging.debug("stop on inactive started")
|
||||
|
@ -20,26 +20,6 @@ kb = keyboard.Controller()
|
||||
offset = 0
|
||||
|
||||
|
||||
def get_crop_coords(window):
|
||||
img = window.get_capture()
|
||||
img = cv2.inRange(img, 0, 1)
|
||||
|
||||
cnt, h = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
|
||||
|
||||
"""
|
||||
code from https://stackoverflow.com/a/45770227/4512396
|
||||
"""
|
||||
for i in range(len(cnt)):
|
||||
area = cv2.contourArea(cnt[i])
|
||||
if 5000 < area < 100000:
|
||||
mask = np.zeros_like(img)
|
||||
cv2.drawContours(mask, cnt, i, 255, -1)
|
||||
x, y, w, h = cv2.boundingRect(cnt[i])
|
||||
return x, y + offset, x + w, y + h - offset
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _update_factor(key, value):
|
||||
full_auto_factors = config.get("full_auto_factors", {})
|
||||
full_auto_factors[key] = value
|
||||
|
@ -3,6 +3,7 @@ import time
|
||||
import tkinter as tk
|
||||
import tkinter.ttk as ttk
|
||||
import typing
|
||||
from functools import partial
|
||||
|
||||
from fishy.gui import update_dialog
|
||||
from ttkthemes import ThemedTk
|
||||
@ -11,6 +12,7 @@ from fishy import helper
|
||||
from fishy.web import web
|
||||
|
||||
from ..constants import fishyqr
|
||||
from ..engine.common import screenshot
|
||||
from ..helper.config import config
|
||||
from .discord_login import discord_login
|
||||
from ..helper.hotkey.hotkey_process import hotkey
|
||||
@ -62,6 +64,7 @@ def _create(gui: 'GUI'):
|
||||
def update():
|
||||
config.delete("dont_ask_update")
|
||||
update_dialog.check_update(gui, True)
|
||||
|
||||
filemenu.add_command(label="Update", command=update)
|
||||
|
||||
def installer():
|
||||
@ -71,6 +74,7 @@ def _create(gui: 'GUI'):
|
||||
else:
|
||||
helper.install_required_addons(True)
|
||||
filemenu.entryconfigure(4, label="Remove FishyQR")
|
||||
|
||||
chaEntry = "Remove FishyQR" if helper.addon_exists(fishyqr[0]) else "Install FishyQR"
|
||||
filemenu.add_command(label=chaEntry, command=installer)
|
||||
menubar.add_cascade(label="Options", menu=filemenu)
|
||||
@ -79,6 +83,26 @@ def _create(gui: 'GUI'):
|
||||
debug_menu.add_command(label="Check QR Value",
|
||||
command=lambda: gui.engine.check_qr_val())
|
||||
|
||||
def toggle_show_grab():
|
||||
new_val = 1 - config.get("show_grab", 0)
|
||||
show_grab_var.set(new_val)
|
||||
config.set("show_grab", new_val)
|
||||
show_grab_var = tk.IntVar()
|
||||
show_grab_var.set(config.get("show_grab", 0))
|
||||
debug_menu.add_checkbutton(label="Show Grab Window", variable=show_grab_var, command=lambda: toggle_show_grab(), onvalue=1)
|
||||
|
||||
def select_sslib(selected_i):
|
||||
config.set("sslib", selected_i)
|
||||
sslib_var.set(selected_i)
|
||||
|
||||
sslib = tk.Menu(debug_menu, tearoff=False)
|
||||
sslib_var = tk.IntVar()
|
||||
sslib_var.set(config.get("sslib", 0))
|
||||
for i, lib in enumerate(screenshot.LIBS):
|
||||
sslib.add_checkbutton(label=lib.__name__, variable=sslib_var,
|
||||
command=partial(select_sslib, i), onvalue=i)
|
||||
debug_menu.add_cascade(label="Screenshot Lib", menu=sslib)
|
||||
|
||||
debug_var = tk.IntVar()
|
||||
debug_var.set(int(config.get('debug', False)))
|
||||
|
||||
@ -90,7 +114,8 @@ def _create(gui: 'GUI'):
|
||||
menubar.add_cascade(label="Debug", menu=debug_menu)
|
||||
|
||||
help_menu = tk.Menu(menubar, tearoff=0)
|
||||
help_menu.add_command(label="Need Help?", command=lambda: helper.open_web("https://github.com/fishyboteso/fishyboteso/wiki"))
|
||||
help_menu.add_command(label="Need Help?",
|
||||
command=lambda: helper.open_web("https://github.com/fishyboteso/fishyboteso/wiki"))
|
||||
help_menu.add_command(label="Donate", command=lambda: helper.open_web("https://paypal.me/AdamSaudagar"))
|
||||
menubar.add_cascade(label="Help", menu=help_menu)
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
"""
|
||||
no imports from fishy itself here, or anything which depends on fishy
|
||||
"""
|
||||
|
||||
|
||||
def singleton_proxy(instance_name):
|
||||
def decorator(root_cls):
|
||||
if not hasattr(root_cls, instance_name):
|
||||
|
@ -7,12 +7,14 @@ import threading
|
||||
import time
|
||||
import traceback
|
||||
import webbrowser
|
||||
from datetime import datetime
|
||||
from hashlib import md5
|
||||
from io import BytesIO
|
||||
from threading import Thread
|
||||
from uuid import uuid1
|
||||
from zipfile import ZipFile
|
||||
|
||||
import cv2
|
||||
import requests
|
||||
from playsound import playsound
|
||||
|
||||
@ -205,3 +207,14 @@ def kill_thread(thread):
|
||||
def print_exc():
|
||||
logging.error(traceback.format_exc())
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def save_img(show_name, img):
|
||||
img_path = os.path.join(os_services.get_documents_path(), "fishy_debug", "imgs", show_name)
|
||||
if not os.path.exists(img_path):
|
||||
os.makedirs(img_path)
|
||||
|
||||
t = time.strftime("%Y.%m.%d.%H.%M.%S")
|
||||
cv2.imwrite(
|
||||
os.path.join(img_path, f"{t}.png"),
|
||||
img)
|
||||
|
@ -14,4 +14,5 @@ keyboard
|
||||
playsound
|
||||
event-scheduler
|
||||
mouse
|
||||
pyautogui
|
||||
mss
|
Loading…
Reference in New Issue
Block a user