переделать окно после переопределенной прямой (True) проблемы с перемещением после изменения размера - PullRequest
1 голос
/ 28 января 2020

После удаления настроек окна по умолчанию tkinter я хочу добавить функциональность для перемещения и изменения размера окна. Ниже приведены ключевые фрагменты кода, которые я взял из нескольких мест и откорректировал. Я пытаюсь сделать так, чтобы, когда мышь находилась ниже 199 пикселей от верхней части окна, реакция на событие заключалась в изменении размера окна (это лучшее, что я мог придумать для двух событий, прикрепленных к мыши). В любом случае, я могу переместить окно, но после изменения размера, когда я не могу двигаться снова, я получаю сообщение об ошибке:

TypeError: unsupported operand type(s) for +: 'int' and 'str'

в этой строке:

self.geometry(self.winfo_width() + 'x' + self.winfo_height() + '+{0}+{1}'.format(event.x_root +xwin,event.y_root +ywin))

Я пытался введите числа, чтобы исправить размер после его изменения размера. Это позволяет мне снова двигаться, но размер окна фиксируется на цифрах, которые я заменяю self.winfo_width() + 'x' + self.winfo_height() +. так что в этой строке должно быть что-то, что я пропускаю работа, которая была бы великолепна Я все еще пытаюсь понять, как работают классы, события и привязки. Я использую python 3,7.

from tkinter import *
import tkinter as tk
from tkinter import ttk

# resize class code take from https://stackoverflow.com/questions/22421888/tkinter-windows-without-title-bar-but-resizable
class Example(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.floater = FloatingWindow(self)
        self.withdraw()


class FloatingWindow(tk.Toplevel):
    def __init__(self, *args, **kwargs):
        tk.Toplevel.__init__(self, *args, **kwargs)

        # fucntion i brought in to close window
        def end_app_2():
            self.destroy()
            quit()

        self.overrideredirect(True)
        self.wm_geometry("400x400")
        self.label = tk.Label(self, text="Grab the lower-right corner to resize")
        self.label.pack(side="bottom", fill="both", expand=True)
        self.canvas_2=Canvas(self,bg='steelblue1')
        self.canvas_2.pack(anchor='s', side='bottom')
        self.title_bar_2 = tk.Frame(self, height=25, bg='SteelBlue1', relief='raised', bd=1)
        self.title_bar_2.pack(anchor='n', fill='x', side="bottom" )
        self.close_button = Button(self.title_bar_2, text='X', command=end_app_2)
        self.close_button.pack( fill='x', side="right" )
        self.grip = ttk.Sizegrip(self)
        self.grip.place(relx=1.0, rely=1.0, anchor="se")
        self.grip.lift(self.label)
        self.grip.bind("<B1-Motion>", self.OnMotion)

        # move window
        def get_pos(event):
            xwin = self.winfo_x()
            ywin = self.winfo_y()
            startx = event.x_root
            starty = event.y_root
            ywin = ywin - starty
            xwin = xwin - startx

            def move_window(event):
                global size_change
                if size_change==True:
                    self.geometry(self.winfo_width() + 'x' + self.winfo_height() + '+{0}+{1}'.format(event.x_root + xwin, event.y_root + ywin))
                else:
                    self.geometry('400x400' + '+{0}+{1}'.format(event.x_root + xwin, event.y_root + ywin))

            if ywin>=-199:
                self.bind('<B1-Motion>', move_window)

        self.bind('<Button-1>', get_pos)


    def OnMotion(self, event):
        x1 = self.winfo_pointerx()
        y1 = self.winfo_pointery()
        x0 = self.winfo_rootx()
        y0 = self.winfo_rooty()
        self.geometry("%sx%s" % ((x1-x0),(y1-y0)))
        global size_change
        size_change=True
        return

app=Example()
size_change=BooleanVar()
app.mainloop()

1 Ответ

2 голосов
/ 28 января 2020

Ваша ошибка должна сказать вам все, что вам нужно здесь.

unsupported operand type(s) for +: 'int' and 'str'

Это говорит о том, что вы не можете добавлять целые числа и строку вместе. Одно простое решение - изменить эту строку:

self.geometry(self.winfo_width() + 'x' + self.winfo_height() + '+{0}+{1}'.format(event.x_root + xwin, event.y_root + ywin))

На это:

self.geometry(str(self.winfo_width()) + 'x' + str(self.winfo_height()) + '+{0}+{1}'.format(event.x_root + xwin, event.y_root + ywin))

Преобразовав целое число, которое winfo возвращает в строку, вы можете объединить без проблем.

Тем не менее, вместо этого я использовал бы format() для всех переменных здесь.

Вот как я бы написал эту строку кода:

self.geometry('{}x{}+{}+{}'.format(self.winfo_width(), self.winfo_height(), event.x_root + xwin, event.y_root + ywin))

Все это сказал, что я вижу несколько других проблем, которые должны быть решены для правильной работы вашего кода.

  1. Вам не нужно использовать оба from tkinter import * и import tkinter as tk. Просто используйте import tkinter as tk. Это предпочтительный метод, так как он помогает предотвратить перезапись методов.

  2. Вам не нужно определять все как атрибут класса, поэтому сокращайте использование self. только там, где это необходимо .

  3. Следуйте стандартному соглашению об именах. Потратьте некоторое время на чтение PEP8.

  4. Ваша функция OnMotion имеет return без причины. Эта строка может быть удалена.

  5. Вы используете функции внутри класса, а также global. Одним из основных преимуществ для класса является возможность избежать global с помощью атрибута класса. Поэтому я переместил вашу переменную size_change в __init__, а затем изменил вашу функцию на метод для get_pos.

  6. В вашей функции OnMotion вы выполняли size_change = True но вы уже определили эту переменную как BooleanVar(), поэтому вам нужно вместо этого установить значение. Вот так: size_change.set(True).

См. Приведенный ниже код:

import tkinter as tk
from tkinter import ttk


class Example(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.floater = FloatingWindow()
        self.withdraw()


class FloatingWindow(tk.Toplevel):
    def __init__(self):
        super().__init__()
        self.overrideredirect(True)
        self.wm_geometry("400x400")
        self.size_change = tk.BooleanVar()
        self.label = tk.Label(self, text="Grab the lower-right corner to resize")
        self.label.pack(side="bottom", fill="both", expand=True)
        canvas_2 = tk.Canvas(self, bg='steelblue1')
        canvas_2.pack(anchor='s', side='bottom')
        title_bar_2 = tk.Frame(self, height=25, bg='SteelBlue1', relief='raised', bd=1)
        title_bar_2.pack(anchor='n', fill='x', side="bottom")
        tk.Button(title_bar_2, text='X', command=self.end_app_2).pack(fill='x', side="right")
        grip = ttk.Sizegrip(self)
        grip.place(relx=1.0, rely=1.0, anchor="se")
        grip.lift(self.label)
        grip.bind("<B1-Motion>", self.on_motion)
        self.bind('<Button-1>', self.get_pos)

    def end_app_2(self):
        self.destroy()

    def get_pos(self, event):
        xwin = self.winfo_x()
        ywin = self.winfo_y()
        startx = event.x_root
        starty = event.y_root
        ywin = ywin - starty
        xwin = xwin - startx

        def move_window(event):
            if self.size_change:
                self.geometry('{}x{}+{}+{}'.format(self.winfo_width(), self.winfo_height(),
                                                   event.x_root + xwin, event.y_root + ywin))
            else:
                self.geometry('400x400' + '+{0}+{1}'.format(event.x_root + xwin, event.y_root + ywin))

        if ywin >= -199:
            self.bind('<B1-Motion>', move_window)

    def on_motion(self, _=None):
        x1 = self.winfo_pointerx()
        y1 = self.winfo_pointery()
        x0 = self.winfo_rootx()
        y0 = self.winfo_rooty()
        self.geometry("%sx%s" % ((x1-x0), (y1-y0)))
        self.size_change.set(True)


if __name__ == '__main__':
    app = Example().mainloop()

Все, что говорит, я думаю, что это сложнее, чем нужно. После попытки решить проблему с помощью метода on_motion, я решил просто переписать все, так как хороший раздел кода даже не требовался для получения функциональности.

Я считаю, что:

if ywin >= -199:
    self.bind('<B1-Motion>', move_window)

Была причиной проблемы после изменения размера.

Попробуйте этот код и дайте мне знать, если у вас есть какие-либо вопросы:

import tkinter as tk
import tkinter.ttk as ttk


class Win(tk.Tk):
    def __init__(self):
        super().__init__()
        self.columnconfigure(0, weight=1)
        self.rowconfigure(2, weight=1)
        self.overrideredirect(True)
        self.wm_geometry("400x400")
        self.minsize(400, 400)
        self.x = 0
        self.y = 0

        title_bar_2 = tk.Frame(self, height=25, bg='SteelBlue1', relief='raised', bd=1)
        title_bar_2.grid(row=0, column=0, sticky='ew')
        tk.Button(title_bar_2, text='X', command=self.destroy).pack(fill='x', side="right")
        self.canvas_2 = tk.Canvas(self, bg='steelblue1')
        self.canvas_2.grid(row=1, column=0)
        tk.Label(self, text="Grab the lower-right corner to resize").grid(row=2, column=0)
        grip = ttk.Sizegrip(self)
        grip.place(relx=1.0, rely=1.0, anchor="se")

        title_bar_2.bind('<ButtonPress-1>', self.button_press)
        title_bar_2.bind('<B1-Motion>', self.move_window)

    def move_window(self, event):
        x = self.winfo_pointerx() - self.x
        y = self.winfo_pointery() - self.y
        self.geometry('+{}+{}'.format(x, y))

    def button_press(self, event):
        self.x = event.x
        self.y = event.y


win = Win()
win.mainloop()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...