Гайд Делаем рендер для чита на tkinter (Python 3.9)

Эксперт
Статус
Оффлайн
Регистрация
9 Апр 2020
Сообщения
1,446
Реакции[?]
674
Поинты[?]
33K
Посмотрел на форумах что там по читами на питоне, там все очень печально так что ждем Maze V7 на днях, а у тех что есть посмотрел реализацию, у кого рендер на tkinter есть весьма важный минус, по ходу статьи расскажу о многих нюансах рендера на tkinter.

Итак, для самого рендера понадобится создать динамический класс с методом для создания оверлея (так мы сможем делать несколько окон для рендера) на котором будем создавать фигуры, оверлею понадобиться отключить обрамления экрана и сделать его на весь экран, все таки для чита делаем, так же установим задний фон для окна и canvas на черный, он все равно не будет виден, тк окно будет прозрачное, ну или почти...

Python:
class Overlay():
    Window = None
    OverlayName = None


    def __init__(self, OverlayName):
        self.OverlayName = OverlayName
        self.CreateOverlay()


    def CreateOverlay(self):
        self.Window = tkinter.Tk()
        self.Window.overrideredirect(True) # Отключаем обрамления оверлея
        self.Window.title(self.OverlayName)
        self.Window.wm_attributes("-topmost", True) # Делаем оверлей по верх всех окно
        self.Window.wm_attributes("-toolwindow", True)
        self.Window.wm_attributes("-transparentcolor", "black")
        self.Window.config(bg = "black")
В инициализацию класса мы передаем имя окно рендера, его конечно видно не будет, но в системе он будет отображаться.
Далее нужно создать методы для рисования разных фигур, но фигуры рисуются непосредственно не на сомом окне, а не холсте Canvas (отдельный элемент tkinter), на самом окне можно рисовать только виджеты по типу: строки, кнопки и тп.
Тогда в оверлее создаем сам canvas, делаем его по размерам на весь экран.
Python:
self.Render = tkinter.Canvas(self.Window, width = win32api.GetSystemMetrics(0), height = win32api.GetSystemMetrics(1), bg = "black", highlightthickness = 0)
self.Render.pack(expand = True)
А теперь эти самые обещанные проблемы tkinter, окно действительно будет прозрачным и по сути мы бы могли уже на этом прозрачном холсте отрисовывать фигуры для чита, только есть ОГРОМНЫЙ минут, когда мы рисуем фигуры, canvas вешает на него события мыши, смекаете чем дело пахнет, ни дай бог пользователь нажмет на левую кнопку мыши и посередине экрана будет часть нарисованной фигуры, canvas сразу переместит фокус на себя и окно игры становится неактивным, на этом моменте я хотел уже переписать рендер на другую библиотеку, но подумал, не может же быть все так печально. Потом вспомнил что я не всю свою жизнь хавал говно но и успел еще выучить кресты, как ренедер реализован у меня в чите, и тут я вспомнял, ебаные стили окна...
Для тех кто не шарит в рендерах, а так как этот гайд для питонистов, а хауди хо об этом не рассказывал, соответственно откуда им знать как ренедер на крестах реализуют, тогда поясняю специально для них.
У винды есть стили окон которые задаются при создании окна, их даже можно менять у стороннего окна.
Так вот для нашего оверлея сделаем следующие стили:
GWL_EXSTYLE - устанавливаем новый стиль окна
WS_EX_LAYERED + WS_EX_TRANSPARENT - создает некую дыру в оверлее, которая позволяет рендерить оверлей поверх всех окон и пропускать событие мыши через оверлей в нижестоящие окна
WS_EX_NOACTIVATE - чтобы оверлей на отображался в табе
Python:
self.Render = tkinter.Canvas(self.Window, width = win32api.GetSystemMetrics(0), height = win32api.GetSystemMetrics(1), bg = "black", highlightthickness = 0)
self.Render.pack(expand = True)

win32gui.SetWindowLong(self.Render.winfo_id(), win32con.GWL_EXSTYLE, win32con.WS_EX_LAYERED | win32con.WS_EX_TRANSPARENT | win32con.WS_EX_NOACTIVATE)
win32gui.SetLayeredWindowAttributes(self.Render.winfo_id(), 0, 255, win32con.LWA_ALPHA)
Теперь следующая проблема, добавив эти стили окна все будет работать как надо НО, теперь возникает проблема со злоебучим canvas, он имеет черный фон на весь экран (тк мы его на весь экран и сделали), что бы исправить это недоразумение обладателям нормальных языков программирования в голову первым делом пришло бы установить альфу canvas на 0, а остальным фигурам на 1, только есть один досадный минус, В TKINTER НЕТ ВОЗМОЖНОСТИ МЕНЯТЬ АЛЬФА-КАНАЛ, что мы будем делать, мы все это хаваем, у нас нет выбора.
Можно решить это проблему частично, хоть ебаный tkinter не дает возможность менять альфа-канал внутри себя, но винда дает возможность менять альфа канал всего оверлея, так и поступим от безысходности. self.Window.wm_attributes("-alpha", 0.4)
Черный фон начинает иметь альфа канал, и не сильно затемняет экран, получился встроенный найт мод, не баг а фича.
Чем сильнее уменьшаем альфа-канал тем лучше будет видно основной экран, но хуже сами фигур, идеальное значение альфа-канала 0.4 - 0.5.
Так же теперь мы не сможем делать несколько оверлеев так как их афлька-канал будет накладоваться друг на друга и будет прям темный экран.
Когда с этой хуйней мы разобрались переходим к следующей.
Для ренедера существуют две основные функции это BeginRender (очищает полотно canvas от нарисованных элементов) и EndRender (обновляет полотно canvas, чтобы отображать новые фигуры)
Python:
def BeginRender(self):
    self.Render.delete("all")


def EndRender(self):
    self.Render.update()
Так же добавим конвертор RGB цветов в HEX, тк tkinter RGB не поддерживает.
C++:
def RGBtoHEX(self, R, G, B):
   return f'#{R:02x}{G:02x}{B:02x}'
Теперь методы отрисовки фигур
Python:
def DrawLine(self, x1, y1, x2, y2, Color):
    self.Render.create_line(x1, y1, x2, y2, fill = Color)


def DrawRect(self, x1, y1, x2, y2, Color):
    self.Render.create_rectangle(x1, y1, x2, y2, outline = Color)


def DrawRectangle(self, x, y, w, h, Color):
    self.DrawLine(x, y, x + w, y, Color)
    self.DrawLine(x, y, x, y + h, Color)
    self.DrawLine(x + w, y, x + w, y + h, Color)
    self.DrawLine(x, y + h, x + w, y + h, Color)


def DrawFilledRectangle(self, x, y, w, h, h2, Color):
    self.Render.create_rectangle(x - 1, y - 1, x + w + 1, y + h + 1, outline = self.RGBtoHEX(255, 255, 255))
    self.Render.create_rectangle(x + w, y + h, x, y + (h - (h2)), fill = Color, outline = Color)


def DrawText(self, x, y, text, Color):
    self.Render.create_text(x, y, text = text, fill = Color, font = ("Calibri bold", 10))


def DrawCircle(self, x, y, r, Color):
    self.Render.create_oval(x - r, y - r, x + r, y + r, outline = Color)
В итоге получаем такую вот конфетку
Полный исходник рендера тут:
Пожалуйста, авторизуйтесь для просмотра ссылки.
 
душнила
Пользователь
Статус
Оффлайн
Регистрация
19 Июн 2019
Сообщения
262
Реакции[?]
46
Поинты[?]
7K
Посмотрел на форумах что там по читами на питоне, там все очень печально так что ждем Maze V7 на днях, а у тех что есть посмотрел реализацию, у кого рендер на tkinter есть весьма важный минус, по ходу статьи расскажу о многих нюансах рендера на tkinter.

Итак, для самого рендера понадобится создать динамический класс с методом для создания оверлея (так мы сможем делать несколько окон для рендера) на котором будем создавать фигуры, оверлею понадобиться отключить обрамления экрана и сделать его на весь экран, все таки для чита делаем, так же установим задний фон для окна и canvas на черный, он все равно не будет виден, тк окно будет прозрачное, ну или почти...

Python:
class Overlay():
    Window = None
    OverlayName = None


    def __init__(self, OverlayName):
        self.OverlayName = OverlayName
        self.CreateOverlay()


    def CreateOverlay(self):
        self.Window = tkinter.Tk()
        self.Window.overrideredirect(True) # Отключаем обрамления оверлея
        self.Window.title(self.OverlayName)
        self.Window.wm_attributes("-topmost", True) # Делаем оверлей по верх всех окно
        self.Window.wm_attributes("-toolwindow", True)
        self.Window.wm_attributes("-transparentcolor", "black")
        self.Window.config(bg = "black")
В инициализацию класса мы передаем имя окно рендера, его конечно видно не будет, но в системе он будет отображаться.
Далее нужно создать методы для рисования разных фигур, но фигуры рисуются непосредственно не на сомом окне, а не холсте Canvas (отдельный элемент tkinter), на самом окне можно рисовать только виджеты по типу: строки, кнопки и тп.
Тогда в оверлее создаем сам canvas, делаем его по размерам на весь экран.
Python:
self.Render = tkinter.Canvas(self.Window, width = win32api.GetSystemMetrics(0), height = win32api.GetSystemMetrics(1), bg = "black", highlightthickness = 0)
self.Render.pack(expand = True)
А теперь эти самые обещанные проблемы tkinter, окно действительно будет прозрачным и по сути мы бы могли уже на этом прозрачном холсте отрисовывать фигуры для чита, только есть ОГРОМНЫЙ минут, когда мы рисуем фигуры, canvas вешает на него события мыши, смекаете чем дело пахнет, ни дай бог пользователь нажмет на левую кнопку мыши и посередине экрана будет часть нарисованной фигуры, canvas сразу переместит фокус на себя и окно игры становится неактивным, на этом моменте я хотел уже переписать рендер на другую библиотеку, но подумал, не может же быть все так печально. Потом вспомнил что я не всю свою жизнь хавал говно но и успел еще выучить кресты, как ренедер реализован у меня в чите, и тут я вспомнял, ебаные стили окна...
Для тех кто не шарит в рендерах, а так как этот гайд для питонистов, а хауди хо об этом не рассказывал, соответственно откуда им знать как ренедер на крестах реализуют, тогда поясняю специально для них.
У винды есть стили окон которые задаются при создании окна, их даже можно менять у стороннего окна.
Так вот для нашего оверлея сделаем следующие стили:
GWL_EXSTYLE - устанавливаем новый стиль окна
WS_EX_LAYERED + WS_EX_TRANSPARENT - создает некую дыру в оверлее, которая позволяет рендерить оверлей поверх всех окон и пропускать событие мыши через оверлей в нижестоящие окна
WS_EX_NOACTIVATE - чтобы оверлей на отображался в табе
Python:
self.Render = tkinter.Canvas(self.Window, width = win32api.GetSystemMetrics(0), height = win32api.GetSystemMetrics(1), bg = "black", highlightthickness = 0)
self.Render.pack(expand = True)

win32gui.SetWindowLong(self.Render.winfo_id(), win32con.GWL_EXSTYLE, win32con.WS_EX_LAYERED | win32con.WS_EX_TRANSPARENT | win32con.WS_EX_NOACTIVATE)
win32gui.SetLayeredWindowAttributes(self.Render.winfo_id(), 0, 255, win32con.LWA_ALPHA)
Теперь следующая проблема, добавив эти стили окна все будет работать как надо НО, теперь возникает проблема со злоебучим canvas, он имеет черный фон на весь экран (тк мы его на весь экран и сделали), что бы исправить это недоразумение обладателям нормальных языков программирования в голову первым делом пришло бы установить альфу canvas на 0, а остальным фигурам на 1, только есть один досадный минус, В TKINTER НЕТ ВОЗМОЖНОСТИ МЕНЯТЬ АЛЬФА-КАНАЛ, что мы будем делать, мы все это хаваем, у нас нет выбора.
Можно решить это проблему частично, хоть ебаный tkinter не дает возможность менять альфа-канал внутри себя, но винда дает возможность менять альфа канал всего оверлея, так и поступим от безысходности. self.Window.wm_attributes("-alpha", 0.4)
Черный фон начинает иметь альфа канал, и не сильно затемняет экран, получился встроенный найт мод, не баг а фича.
Чем сильнее уменьшаем альфа-канал тем лучше будет видно основной экран, но хуже сами фигур, идеальное значение альфа-канала 0.4 - 0.5.
Так же теперь мы не сможем делать несколько оверлеев так как их афлька-канал будет накладоваться друг на друга и будет прям темный экран.
Когда с этой хуйней мы разобрались переходим к следующей.
Для ренедера существуют две основные функции это BeginRender (очищает полотно canvas от нарисованных элементов) и EndRender (обновляет полотно canvas, чтобы отображать новые фигуры)
Python:
def BeginRender(self):
    self.Render.delete("all")


def EndRender(self):
    self.Render.update()
Так же добавим конвертор RGB цветов в HEX, тк tkinter RGB не поддерживает.
C++:
def RGBtoHEX(self, R, G, B):
   return f'#{R:02x}{G:02x}{B:02x}'
Теперь методы отрисовки фигур
Python:
def DrawLine(self, x1, y1, x2, y2, Color):
    self.Render.create_line(x1, y1, x2, y2, fill = Color)


def DrawRect(self, x1, y1, x2, y2, Color):
    self.Render.create_rectangle(x1, y1, x2, y2, outline = Color)


def DrawRectangle(self, x, y, w, h, Color):
    self.DrawLine(x, y, x + w, y, Color)
    self.DrawLine(x, y, x, y + h, Color)
    self.DrawLine(x + w, y, x + w, y + h, Color)
    self.DrawLine(x, y + h, x + w, y + h, Color)


def DrawFilledRectangle(self, x, y, w, h, h2, Color):
    self.Render.create_rectangle(x - 1, y - 1, x + w + 1, y + h + 1, outline = self.RGBtoHEX(255, 255, 255))
    self.Render.create_rectangle(x + w, y + h, x, y + (h - (h2)), fill = Color, outline = Color)


def DrawText(self, x, y, text, Color):
    self.Render.create_text(x, y, text = text, fill = Color, font = ("Calibri bold", 10))


def DrawCircle(self, x, y, r, Color):
    self.Render.create_oval(x - r, y - r, x + r, y + r, outline = Color)
В итоге получаем такую вот конфетку
Полный исходник рендера тут:
Пожалуйста, авторизуйтесь для просмотра ссылки.
Пожалуйста, авторизуйтесь для просмотра ссылки.
думаю это полегче будет для рендера
 
Эксперт
Статус
Оффлайн
Регистрация
9 Апр 2020
Сообщения
1,446
Реакции[?]
674
Поинты[?]
33K
Последнее редактирование:
Сверху Снизу