Как скопировать содержимое рамки черепахи, встроенной в холст tkinter - PullRequest
0 голосов
/ 28 апреля 2018

Я пытаюсь сохранить текущее состояние холста tkinter для отображения позже. Вот метод, который я пытался использовать deepcopy:

from tkinter import *
from copy import deepcopy
root=Tk()
cv=Canvas(root)
cv.pack()
cvcopy=deepcopy(cv)
mainloop()

Однако строка cvcopy=deepcopy(cv) создает ошибку:

Traceback (most recent call last):
  File "C:/Users/Fred/Desktop/painting/mcve.py", line 6, in <module>
    cvcopy=deepcopy(cv)
  File "C:\python files\lib\copy.py", line 173, in deepcopy
    y = _reconstruct(x, rv, 1, memo)
  File "C:\python files\lib\copy.py", line 295, in _reconstruct
    state = deepcopy(state, memo)
  File "C:\python files\lib\copy.py", line 146, in deepcopy
    y = copier(x, memo)
  File "C:\python files\lib\copy.py", line 235, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "C:\python files\lib\copy.py", line 173, in deepcopy
    y = _reconstruct(x, rv, 1, memo)
  File "C:\python files\lib\copy.py", line 295, in _reconstruct
    state = deepcopy(state, memo)
  File "C:\python files\lib\copy.py", line 146, in deepcopy
    y = copier(x, memo)
  File "C:\python files\lib\copy.py", line 235, in _deepcopy_dict
    y[deepcopy(key, memo)] = deepcopy(value, memo)
  File "C:\python files\lib\copy.py", line 173, in deepcopy
    y = _reconstruct(x, rv, 1, memo)
  File "C:\python files\lib\copy.py", line 280, in _reconstruct
    y = callable(*args)
  File "C:\python files\lib\copyreg.py", line 88, in __newobj__
    return cls.__new__(cls, *args)
TypeError: object.__new__(tkapp) is not safe, use tkapp.__new__()

Потому что, очевидно, вы не можете просто создать новый виджет, подобный этому. Вот код, который я использую для встраивания и рисования на экране turtle:

cv = tkinter.Canvas(self.root,width=300,height=300)
cv.pack()
t = turtle.RawTurtle(cv)
s = t.getscreen()
def toggledown():
    if t.isdown():
        t.penup()
    else:
        t.pendown()
t.speed(0)
t.ondrag(t.goto)
s.onclick(t.goto)
s.onkey(toggledown, 'space')
s.listen()

Кто-нибудь знает, как скопировать экран и добавить его еще куда-нибудь?

Ответы [ 3 ]

0 голосов
/ 28 апреля 2018

Нет способа скопировать виджет, однако, если вы одновременно создаете идентичный виджет, но без вызова менеджера геометрии, вы можете добавить его на экран позже:

from tkinter import *
root = Tk()
cv = Canvas(self.root, width=300, height=300)
cv.pack()
copy = Canvas(otherframe, width=300, height=300)
t = turtle.RawTurtle(cv)
s = t.getscreen()
ct = turtle.RawTurtle(copy)
cs = ct.getscreen()

def toggledown():
    if t.isdown():
            t.penup()
            ct.penup()  #no point testing twice since they're the same
        else:
            t.pendown()
            ct.pendown()

def goto(x, y):
    t.goto(x, y)
    ct.goto(x, y)

Button(self.root, text='Copy', command=copy.pack).pack()
t.speed(0)
ct.speed(0)
t.ondrag(goto)  #the copy will go to the same place
s.onclick(goto)
s.onkey(toggledown, 'space')
s.listen()
mainloop()

В этом случае вы также можете сэкономить время, создав класс для полотен:

class Drawing(object):

    def __init__(self, master, otherframe):
        self.cv = Canvas(master, width=300, height=300)
        self.cv.pack()
        self.copy = Canvas(otherframe, width=300, height=300)
        self.t.RawTurtle(self)
        self.s = self.t.getscreen()
        self.ct.RawTurtle(self)
        self.cs = self.ct.getscreen()
        self.ct.speed(0)
        self.t.speed(0)
        self.t.ondrag(self.goto) #the copy will go to the same place
        self.s.onclick(self.goto)
        self.s.onkey(self.toggledown, 'space')
        self.s.listen()

    def toggledown(self):
        if self.t.isdown():
            self.t.penup()
            self.ct.penup()
        else:
            self.t.pendown()
            self.ct.pendown()

    def goto(self, x, y):
        self.t.goto(x, y)
        self.ct.goto(x, y)

    def addCopy(self):
        self.copy.pack()
0 голосов
/ 29 апреля 2018

Вот что-то, что, кажется, может выполнить то, что вы хотите. Это не полномасштабный пример, а скорее демонстрация одного подхода к достижению вашей цели - сделать копию.

В вашем вопросе нет кода, который на самом деле что-то рисует на Canvas, поэтому я позаимствовал его из учебника по использованию tkinter, который я нашел, чтобы получить Canvas с несколькими виджетами на нем в целях иллюстрации. Он создает окно, похожее на это, которое имеет один Button и один Canvas виджет, содержащий только три разноцветных виджета прямоугольника:

screenshot of program running

Вот демонстрационный код, показывающий, как перебирать все виджеты, в настоящее время Canvas:

from pprint import pprint, pformat
from tkinter import Button, Tk, Canvas, Frame, BOTH

class Example(Frame):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.master.title("Colours")
        self.pack(fill=BOTH, expand=1)

        button = Button(self, text="Copy", command=self.copy_canvas)
        button.pack()

        self.canvas = Canvas(self)
        self.canvas.create_rectangle(30, 10, 120, 80,
                                     outline="#fb0", fill="#fb0")
        self.canvas.create_rectangle(150, 10, 240, 80,
                                     outline="#f50", fill="#f50")
        self.canvas.create_rectangle(270, 10, 370, 80,
                                     outline="#05f", fill="#05f")
        self.canvas.pack(fill=BOTH, expand=1)

    def copy_canvas(self):
        # Iterate through all the items in self.canvas and print each
        # one's type and options (which could be used to recreate it).
        for id in self.canvas.find_all():
            item_type = self.canvas.type(id)
            options = self.canvas.itemconfigure(id)
            formatted_options = pformat(options, indent=4)
            print('id: {}, type: {!r}\n{}'.format(
                    id, item_type, formatted_options))


def main():
    root = Tk()
    ex = Example()
    root.geometry("400x100+300+300")
    root.mainloop()


if __name__ == '__main__':
    main()

и вот что он печатает при нажатии кнопки Copy :

id: 1, type: 'rectangle'
{   'activedash': ('activedash', '', '', '', ''),
    'activefill': ('activefill', '', '', '', ''),
    'activeoutline': ('activeoutline', '', '', '', ''),
    'activeoutlinestipple': ('activeoutlinestipple', '', '', '', ''),
    'activestipple': ('activestipple', '', '', '', ''),
    'activewidth': ('activewidth', '', '', '0.0', '0.0'),
    'dash': ('dash', '', '', '', ''),
    'dashoffset': ('dashoffset', '', '', '0', '0'),
    'disableddash': ('disableddash', '', '', '', ''),
    'disabledfill': ('disabledfill', '', '', '', ''),
    'disabledoutline': ('disabledoutline', '', '', '', ''),
    'disabledoutlinestipple': ('disabledoutlinestipple', '', '', '', ''),
    'disabledstipple': ('disabledstipple', '', '', '', ''),
    'disabledwidth': ('disabledwidth', '', '', '0.0', '0'),
    'fill': ('fill', '', '', '', '#fb0'),
    'offset': ('offset', '', '', '0,0', '0,0'),
    'outline': ('outline', '', '', 'black', '#fb0'),
    'outlineoffset': ('outlineoffset', '', '', '0,0', '0,0'),
    'outlinestipple': ('outlinestipple', '', '', '', ''),
    'state': ('state', '', '', '', ''),
    'stipple': ('stipple', '', '', '', ''),
    'tags': ('tags', '', '', '', ''),
    'width': ('width', '', '', '1.0', '1.0')}
id: 2, type: 'rectangle'
{   'activedash': ('activedash', '', '', '', ''),
    'activefill': ('activefill', '', '', '', ''),
    'activeoutline': ('activeoutline', '', '', '', ''),
    'activeoutlinestipple': ('activeoutlinestipple', '', '', '', ''),
    'activestipple': ('activestipple', '', '', '', ''),
    'activewidth': ('activewidth', '', '', '0.0', '0.0'),
    'dash': ('dash', '', '', '', ''),
    'dashoffset': ('dashoffset', '', '', '0', '0'),
    'disableddash': ('disableddash', '', '', '', ''),
    'disabledfill': ('disabledfill', '', '', '', ''),
    'disabledoutline': ('disabledoutline', '', '', '', ''),
    'disabledoutlinestipple': ('disabledoutlinestipple', '', '', '', ''),
    'disabledstipple': ('disabledstipple', '', '', '', ''),
    'disabledwidth': ('disabledwidth', '', '', '0.0', '0'),
    'fill': ('fill', '', '', '', '#f50'),
    'offset': ('offset', '', '', '0,0', '0,0'),
    'outline': ('outline', '', '', 'black', '#f50'),
    'outlineoffset': ('outlineoffset', '', '', '0,0', '0,0'),
    'outlinestipple': ('outlinestipple', '', '', '', ''),
    'state': ('state', '', '', '', ''),
    'stipple': ('stipple', '', '', '', ''),
    'tags': ('tags', '', '', '', ''),
    'width': ('width', '', '', '1.0', '1.0')}
id: 3, type: 'rectangle'
{   'activedash': ('activedash', '', '', '', ''),
    'activefill': ('activefill', '', '', '', ''),
    'activeoutline': ('activeoutline', '', '', '', ''),
    'activeoutlinestipple': ('activeoutlinestipple', '', '', '', ''),
    'activestipple': ('activestipple', '', '', '', ''),
    'activewidth': ('activewidth', '', '', '0.0', '0.0'),
    'dash': ('dash', '', '', '', ''),
    'dashoffset': ('dashoffset', '', '', '0', '0'),
    'disableddash': ('disableddash', '', '', '', ''),
    'disabledfill': ('disabledfill', '', '', '', ''),
    'disabledoutline': ('disabledoutline', '', '', '', ''),
    'disabledoutlinestipple': ('disabledoutlinestipple', '', '', '', ''),
    'disabledstipple': ('disabledstipple', '', '', '', ''),
    'disabledwidth': ('disabledwidth', '', '', '0.0', '0'),
    'fill': ('fill', '', '', '', '#05f'),
    'offset': ('offset', '', '', '0,0', '0,0'),
    'outline': ('outline', '', '', 'black', '#05f'),
    'outlineoffset': ('outlineoffset', '', '', '0,0', '0,0'),
    'outlinestipple': ('outlinestipple', '', '', '', ''),
    'state': ('state', '', '', '', ''),
    'stipple': ('stipple', '', '', '', ''),
    'tags': ('tags', '', '', '', ''),
    'width': ('width', '', '', '1.0', '1.0')}
0 голосов
/ 28 апреля 2018

Ты не можешь делать то, что хочешь. Виджеты не являются чистыми объектами Python. Большая часть их состояния находится во встроенном интерпретаторе tcl. Вы просто не можете делать копии виджетов tkinter следующим образом.

Короче говоря, нет способа сделать копию виджета tkinter. Лучшее, что вы можете сделать, - это использовать метод configure, чтобы получить все параметры конфигурации для виджета, а затем создать новый виджет с такими же параметрами. Даже при этом он не будет хранить внутреннее состояние, например рисунки на холсте, текст внутри текстового виджета и т. Д.

...