Основной метод для вызова пользовательского объекта в Python - PullRequest
0 голосов
/ 09 января 2019

Я пытался исправить что-то, связанное с тем, что возвращает объект, когда вы используете объект в качестве аргумента.

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

Я могу объяснить это намного лучше с помощью кода:

class CustomFrame(object):
    def __init__(self,**args):
        #code that works

        #The next is an external object that have a variable that I need to call externally.
        #The variable is "interior"
        self.externalobject = anotherobject('random')

cfr1 = CustomFrame()

label1 = Label(cfr1)

Теперь я хочу использовать «self.externalobject.interior» в качестве родителя для label1 Но я хочу сделать это дружелюбным, просто вызывая "cfr1" вместо "self.externalobject.interior"

Я знаю, что если я использую метод call и верну нужное мне значение, оно будет работать, если я передам "cfr1" как функцию, но я хочу сделать его как можно более Pythonic .

Так что мне нужно знать, есть ли другой специальный метод или что-то, чтобы изменить то, что он возвращает.

EDIT: Итак, это часть кода, который я использую:

Это код рамки с вертикальной прокруткой (не мой код).

class VerticalScrolledFrame(Frame):
    """A pure Tkinter scrollable frame that actually works!
    * Use the 'interior' attribute to place widgets inside the scrollable frame
    * Construct and pack/place/grid normally
    * This frame only allows vertical scrolling

    """
    def __init__(self, parent, bg, *args, **kw):
        Frame.__init__(self, parent, *args, **kw)            

        # create a canvas object and a vertical scrollbar for scrolling it
        vscrollbar = Scrollbar(self, orient=VERTICAL)
        canvas = Canvas(self, bd=0, highlightthickness=0,
                        yscrollcommand=vscrollbar.set,bg=bg)
        vscrollbar.config(command=canvas.yview)
        canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)

        # reset the view
        canvas.xview_moveto(0)
        canvas.yview_moveto(0)

        # create a frame inside the canvas which will be scrolled with it
        self.interior = interior = Frame(canvas,bg=bg)
        interior_id = canvas.create_window(0, 0, window=interior,
                                           anchor=NW)

        a = Frame(self.interior,height=10,bg=dynamicBackground())
        a.pack()

        def canvasscroll(event):
            canvas.yview('scroll',int(-1*(event.delta/120)), "units")

        def _configure_canvas(event):
            a.configure(height=10)
            a.update()
            mylist = interior.winfo_children()
            for i in mylist:
                lasty=i.winfo_height()+i.winfo_y()
            a.configure(height=lasty)
            if interior.winfo_reqwidth() != canvas.winfo_width():
                # update the inner frame's width to fill the canvas
                canvas.itemconfigure(interior_id, width=canvas.winfo_width())
            if canvas.winfo_height()<lasty:
                vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
                canvas.config(scrollregion=(0,0,0,lasty))
                canvas.bind_all("<MouseWheel>", canvasscroll)
            else:
                canvas.unbind_all("<MouseWheel>")
                try:
                    vscrollbar.pack_forget()
                except:
                    pass
                canvas.config(scrollregion=(0,0,0,0))


        canvas.bind('<Configure>', _configure_canvas)

А вот код для моего пользовательского объекта:

class UtilityPanel(object):
    def __init__(self,parent='*',title='Main Title',state='normal',bg='red'):
        super().__init__()
        if parent != '*':
            self.parent=parent
        else:
            raise TypeError('You must specify a parent for this widget.')
        self.title=title

        global subpanels

        if len(subpanels) == 0:
            self.panelid = 'sp-1'
            subpanels.append('sp-1')
        else:
            self.panelid = 'sp-'+str(int(subpanels[-1].split('-')[1])+1)
            subpanels.append(self.panelid)

        self.panel = VerticalScrolledFrame(self.parent,bg=bg,width=600,height=200,name=self.panelid)

    def __call__(self,name):
        return(self.panel.interior)

    def pack(self):
        self.panel.place(x=300)
        global activepanel
        activepanel = self.panelid

Итак, если я передаю аргумент, как label1 = Label(cfr1.panel.interior), он работает, но я хочу, чтобы он работал, просто передавая cfr1 в качестве аргумента.

Ответы [ 3 ]

0 голосов
/ 09 января 2019

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

Изменить это:

class CustomFrame(object):
    def __init__(self,**args):

К этому:

class CustomFrame(Frame):
    def __init__(self,**args):
        super().__init__()

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

На основе ваших комментариев ниже попробуйте это:

class CustomFrame(anotherobject):
    def __init__(self,**args):
        super().__init__()

Это должно наследовать все методы и атрибуты этого объекта.

0 голосов
/ 09 января 2019

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

(Отказ от ответственности: я никогда не использовал tkinter, поэтому я не могу сказать, действительно ли это работает в контексте этой структуры. Этот пример просто демонстрирует концепцию, которую я пытаюсь изложить здесь.)

class CustomLabel(tk.Label):

    def __init__(self, master, **options):
        if isinstance(master, CustomFrame):
            master = master.externalobject.interior
        super().__init__(master=master, **options)

С этим вы сможете использовать CustomLabel(cfr1).

Однако я решительно поддерживаю сообщение, которое @doctorlove передает в своем ответе. ИМХО, самый питонический способ действительно был бы Label(cfr1.externalobject.interior), предлагаемый вами подход с использованием __call__ или свойство внутри CustomFrame, которое обеспечивает ярлык для externalobject.interior:

@property
def interior(self):
    return self.externalobject.interior

который вы бы использовали так Label(crf1.interior)

0 голосов
/ 09 января 2019

Вы пытаетесь неявно привести / привести один тип объекта к другому.

Если вы хотите быть "питоном", вспомните,

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
...

Неявное выполнение вещей может вызвать проблемы.

Я не вижу ничего плохого в том, чтобы использовать магическую функцию call для получения нужного атрибута или начать поиск свойств и декораторов.

Полный список магических методов здесь

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