Ответы пользователя по тегу Tkinter
  • Tkinter. Какой метод позиционирование лучше выбрать .pack / .place / .grid?

    Vindicar
    @Vindicar
    RTFM!
    Помести кнопки в Frame, Frame прикрути через pack(). Кнопки внутри фрейма - можно тоже через pack, можно еще как.
    Единственное, при добавлении новых элементов нужно будет сообразить, как указать, чтобы они паковались ДО фрейма с кнопками.

    EDIT: Заверни панель кнопок в ещё один Frame, и помести эту панель внутри внешнего Frame через pack(side='top', anchor='center'). Но учитывая, что у тебя в окне и так уже используется pack(side='top'), то можно обойтись без внешнего Frame - просто убери expand/fill с панели кнопок и запакуй её наверх.
    Пример кода с внешней панелью:
    import tkinter as tk
    
    
    root = tk.Tk()
    root.geometry('200x100')
    frame_outer = tk.Frame(root, bg='blue')
    frame_outer.pack(expand=True, fill='both')
    
    frame = tk.Frame(frame_outer, bg='green')
    btn1 = tk.Button(frame, text='Button1')
    btn2 = tk.Button(frame, text='Button2')
    btn1.pack(side='left', padx=5, pady=5)
    btn2.pack(side='left', padx=5, pady=5)
    frame.pack(side='top', anchor='center')
    
    root.mainloop()
    Ответ написан
    3 комментария
  • Как в название одной переменной внести значение другой переменной?

    Vindicar
    @Vindicar
    RTFM!
    Это можно сделать, но в 99% случаев это не нужно делать. Во-первых, пользователь может ввести строку, которая не является корректным идентификатором. Во-вторых, появляется риск нечаянно перезаписать переменную, которую мы не хотели трогать.
    Вместо этого следует использовать словарь (dict), где ключ - имя "переменной", а значение - её содержимое. Это куда более контролируемо.
    Нет, я хочу выстрелить себе в ногу, и санитары меня не остановят!

    Функция globals() возвращает словарь глобальных переменных, а locals() - словарь локальных.
    При этом эти словари - не копии, а, можно сказать, оригиналы. Добавишь в них новый ключ - появится новая переменная. Вот пример в интерактивной консоли питона:
    Python 3.11.7 (tags/v3.11.7:fa7a6f2, Dec  4 2023, 19:24:49) [MSC v.1937 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> l = locals()
    >>> l['x'] = 1
    >>> x
    1
    >>>

    Ответ написан
    Комментировать
  • Как при закрытии главного окна закрывались все остальные окна?

    Vindicar
    @Vindicar
    RTFM!
    class MainWindow(tk.Tk):
        """Главное окно приложения."""
        def __init__(self):
            """Конструктор вызывается при создании окна"""
            super().__init__()
            self.title('Моё окно')
            self.protocol('WM_DELETE_WINDOW', self.__done)  # прописываем реакцию на закрытие окна
            ...  # тут создаёшь элементы управления и проводишь прочую инициализацию
            # в том числе создаёшь и запоминаешь вспомогательные окна
    
        def __done(self):
            """Вызовется при попытке закрытия окна"""
            ...  # тут делаешь что тебе нужно при закрытии
            # например, закрываешь вспомогательные окна
            self.destroy()  # если не сделать этот вызов, окно на самом деле не закроется - иногда это нужно
    
    if __name__ == '__main__':
        root = MainWindow()
        root.mainloop()
    Ответ написан
    2 комментария
  • Как рисовать поверх фото в tkinter?

    Vindicar
    @Vindicar
    RTFM!
    Вместо того, чтобы изобретать велосипед, используй элемент Canvas. Он и рисовать позволяет, и изображение вставить.
    Ответ написан
    Комментировать
  • Как мне правильно запустить поток с ткинтером?

    Vindicar
    @Vindicar
    RTFM!
    root.withdraw() для скрытия и root.deiconify() для показа пробовал?
    Ответ написан
    1 комментарий
  • Как принимать значение entry в customtkinter?

    Vindicar
    @Vindicar
    RTFM!
    Как ни странно, почитать документацию.
    Ответ написан
    Комментировать
  • Как создавать фреймы в неопределённом кол-ве?

    Vindicar
    @Vindicar
    RTFM!
    Простой вариант - складывай нужные объекты в список. Если что, можно сделать список кортежей.
    Чуть более сложный вариант - опиши класс, описывающий пару кнопка-label, и реализующий логику выбора файла, и складывай в список экземпляры этого класса.
    Ответ написан
    Комментировать
  • Как создать выпадающий список с данными из postgres на python?

    Vindicar
    @Vindicar
    RTFM!
    select "Route_num" from "Route TB"
    Во-первых, ты кавычки не перепутал? В SQL вроде имена столбцов и таблиц экранируются `бэктиками`, а не "кавычками".
    Во-вторых, ты просишь выбрать одно значение на каждой строке. Но в то же время обращаешься к item[1], т.е. ко второму элементу строки. Которого быть не может, так как ты выбираешь только один.
    Ответ написан
    6 комментариев
  • Попытался перенести код на tkinter в формат ооп, до этого выводил всё корректно, сейчас же выводит только пустое окно, что делать?

    Vindicar
    @Vindicar
    RTFM!
    1. Оформи код. Кнопка </> в помощь. Сейчас нечитаемо.
    2. Включить голову и разобраться, как же работает графический интерфейс (потому что принципы одинаковые в любом языке практически). Ну и немножко подучить Питон на более простых примерах.

    root_changes_2.mainloop() запускает основной рабочий цикл. В нём программа получает сообщения от ОС о действиях пользователя и реагирует на них. Цикл прерывается, когда закрывается основное окно (ты хранишь ссылку на него в root_changes_2).
    Как следствие, программа будет стоять на этой строке, пока ты не закроешь окно, и ТОЛЬКО ПОТОМ перейдёт к объявлению класса Main_window.

    Это объявление, разумеется, ничего не даёт - ведь ты просто объявил класс, но не создал его экземпляр. А после объявления класса у тебя ничего нет, поэтому скрипт завершает работу.
    Я ФЗ что такое ttkbootstrap, поэтому покажу простой пример на голом tkinter.

    import tkinter as tk
    import tkinter.messagebox as tkmb
    
    class MainWindow(tk.Tk):
        """Главное окно программы наследуется от Tk. 
        Вспомогательные окна - от TopLevel.
        Составные элементы управления - от Frame."""
        
        def __init__(self):
            """Метод __init__() автоматически вызывается при создании нового экземпляра класса."""
            super().__init__()  # обязательно вызываем конструктор родительского класса!
            # при закрытии нашего окна вызовется метод __done()
            # если эта функциональность не нужна, можно убрать эту строку
            self.protocol('WM_DELETE_WINDOW', self.__done)  
            # пример создания элементов управления
            # имена, начинающиеся с __, не видны снаружи класса
            # мы присваиваем не просто переменным, а полям объекта. Эти поля будут видны в других методах.
            self.__entry = tk.Entry(self)  # поле ввода будет вложено непосредственно в наше окно
            self.__entry.pack(side=tk.TOP, fill=tk.X, expand=True)
            self.__btn = tk.Button(self, text='Нажми меня', command=self.__btn_pressed)  # кнопка и реакция на неё
            self.__btn.pack(side=tk.TOP)
        
        def __done(self):
            """Мы прописали вызов этого метода при закрытии окна. 
            Тут мы можем корректно завершить работу нашей программы, а потом подтвердить закрытие."""
            self.destroy()  # подтверждаем закрытие. Если этот вызов не сделать, окно не закроется. Иногда это нужно.
        
        def __btn_pressed(self):
            """Реакция на кнопку."""
            text = self.__entry.get()  # мы можем обращаться к полям объекта
            tkmb.showinfo(
                title='Вы ввели',
                message=text,
                parent=self
            )
    
    
    if __name__ == '__main__':  
        # это условие выполнится, только если скрипт запущен непосредственно
        # оно не выполнится, если скрипт импортируется другим скриптом
        # поэтому в такое условие обычно заворачивают "тело" скрипта
        wnd = MainWindow()  # создаём окно
        wnd.mainloop()  # главный рабочий цикл
        # сюда управление будет передано после закрытия окна
        print('Окно закрыто, пока!')
    Ответ написан
  • Как исправить ошибку tkinter Python?

    Vindicar
    @Vindicar
    RTFM!
    Значит, ты в какой-то момент делаешь в глобальной области видимости присваивание вида execute = tkinter.Button(...)
    Ответ написан
    1 комментарий
  • Как сделать консоль в tkinter python?

    Vindicar
    @Vindicar
    RTFM!
    Создай многострочное поле ввода, у запущенного процесса периодически опрашивай стандартный вывод. Если удалось что-то прочитать - добавляй прочитанное в поле ввода.
    Ответ написан
    Комментировать
  • Почему энтри объект игнорирует запросы?

    Vindicar
    @Vindicar
    RTFM!
    зашитая в кнопку команда объектом энтри игнорируется

    Ты в обработчике кнопки сам создаёшь новый экземпляр класса Put_Entry (который является виджетом), нигде его не размещаешь, а просто что-то с ним делаешь. Конечно, уже существующие экземпляры от этого никак не изменяются - твоя кнопка про них ничего не знает и никак их не трогает.

    Короче, читай учебник, для начала. У тебя, похоже, полнейшая каша в голове. Разбирайся в понятиях области видимости и времени жизни, для начала - тогда будет понятнее, что где хранить и что куда передавать.
    Не говоря уже о том, что я тебе уже два раза показывал, как хоть немного правильно создавать свои UI-классы, но как об стенку горох.
    Ответ написан
    4 комментария
  • Метод insert не работает, как исправить?

    Vindicar
    @Vindicar
    RTFM!
    Ну тут сразу возникает вопрос: какую задачу выполняет этот метод. Ты говоришь, что метод используется другим классом. Окей, это норма. Для чего он используется? Какую задачу другой класс хочет решить вызовом этого метода?
    Вопрос связан с тем, что ты почему-то создаёшь элементы управления в методе put_wigets(). По идее, если твой класс представляет собой группу элементов управления, он должен создавать дочерние элементы в своём конструкторе, и сохранять ссылки на них в приватные (ну, насколько это можно в питоне) поля класса. Методы класса должны к этим полям обращаться. Исходи из того, что локальная переменная в методе (как Ent_numberBox ) живёт только во время вызова этого метода, и не должна содержать ничего, что переживёт этот метод (если это не возвращаемое значение).

    Например, так:
    import tkinter as tk
    
    class InputBox(tk.Frame):
        def __init__(self, master, **kwargs):
            # вызываем унаследованный конструктор
            super().__init__(master, **kwargs)
            # переменные tkinter можно связывать с элементами управления
            # тогда изменение в переменной отобразится в элементе, и наоборот
            # это бывает удобнее, чем дёргать элемент напрямую
            # а ещё можно самим реагировать на изменения значения такой переменной
            self.__value = tk.StringVar()
            # наше поле ввода
            self.__entry = tk.Entry(self, justify=tk.RIGHT, width=22, textvariable=self.__value)
            self.__entry.pack(side=tk.RIGHT, padx=10, pady=15)
            # метка с пояснением
            self.__helptext = tk.Label(self, bg=self['background'], height=3, text="Введите мат. выражение:")
            self.__helptext.pack(side=tk.LEFT)
        
        def get_value(self) -> str:
            """Возвращает введённую строку."""
            return self.__value.get()  # метод get() позволяет прочитать значение переменной tkinter
        
        def set_value(self, value: str) -> None:
            """Заменяет содержимое введённой строки новым."""
            self.__value.set(value)  # метод set() позволяет задать новое значение для переменной
            
        def append(self, symbol: str) -> None:
            """Добавляет символ(ы) к уже введённой строке."""
            self.__value.set(self.__value.get() + symbol)
    
    
    if __name__ == '__main__':
        root = tk.Tk()
        box = InputBox(root)
        box.pack(side=tk.TOP, expand=True, fill=tk.BOTH)
        box.set_value('Hello')
        box.append(', world!')
        root.mainloop()


    Я в этом примере исходил из своего понимания твоей задачи.
    Если это поле ввода, то мы хотим иметь возможность получить введённое значение. Поэтому метод get_value().
    Также ты упомянул необходимость в том, чтобы изменить это значение извне, из другого класса. Значит, нужен метод set_value().
    Также, если это - часть калькулятора, у тебя будет часто встречаться задача "добавить один символ в конец строки". Это можно реализовать через пару get_value()/set_value(), но будет удобнее иметь отдельный метод append().
    Один метод - одна решаемая задача.
    Ответ написан
    3 комментария
  • Кнопка и окно ввода в разных классах, не получается добавить кнопке возможность вставлять знаки в Entry объект?

    Vindicar
    @Vindicar
    RTFM!
    Во-первых, какой виджет за что отвечает? Что такое inputBox? В чём его роль? В идеале одним предложением.

    Во-вторых, не следует лазать в кишки класса без нужды. Вместо этого имеет смысл добавить этому классу удобный метод, который сделает всё что нужно, с пониманием внутреннего устройства класса. Тогда те, кто класс использует, не должны будут знать его внутреннее устройство - только его видимый интерфейс (методы, свойства). Это называется инкапсуляция.
    Ответ написан
    2 комментария
  • Странная не последовательная работа функции в tkinter python?

    Vindicar
    @Vindicar
    RTFM!
    Потому что перерисовка окна - это отдельное событие. Когда ты изменяешь текст метки, она помечает своё окно как нуждающееся в перерисовке. Да, каждый элемент управления - это тоже окно. Это идёт из нижележащего оконного менеджера, и под виндой, и под никсами. Это сделано, чтобы не перерисовывать окно двести раз, если ты делаешь серию изменений в интерфейсе.
    Эта перерисовка будет выполнена после того, как закончит выполняться твой обработчик события.
    Ты можешь попробовать вызвать label1.update_idletasks() после изменения текста, чтобы попросить tkinter перерисовать окно немедленно.
    Ответ написан
    1 комментарий
  • Как добавить готовый график в Figure (matplotlib)?

    Vindicar
    @Vindicar
    RTFM!
    Ты можешь добраться до фигуры так
    from matplotlib import pyplot
    plot: Plot = ...  # тут твой Plot
    fig: pyplot.Figure = plot.backend.fig

    И дальше работать с ней в твоём GUI, как обычно.

    Альтернативно, можно почитать исходники класса MatplotlibBackend, сделать его копию, и модифицировать её так, чтобы вместо
    self.fig = self.plt.figure(figsize=parent.size)
    твой бэкэнд принимал Figure как параметр конструктора.
    Ответ написан
    Комментировать
  • Как исправить ошибку взаемодействия Дискорд бота и окна?

    Vindicar
    @Vindicar
    RTFM!
    #
        loop = asyncio.get_event_loop()
        loop.create_task(process_bot_queue())
        loop.run_forever()

    Что вообще вот этот код делает в теле программы, если у тебя вызов create_window() не вернёт управление, пока окно GUI не будет закрыто?
    Ответ написан
    Комментировать
  • Как исправить ошибку взаимодействия окна и дискорд бота?

    Vindicar
    @Vindicar
    RTFM!
    В коде нет работы с потоками, ты даже не импортируешь threading.
    Нужно понимать три вещи:
    1. Рабочий цикл бота займёт весь поток, равно как и рабочий цикл tkinter. Совместить их практически невозможно (технически можно, но это очень нетривиально).
    2. Элементы GUI должны создаваться и использоваться строго в одном и том же потоке.
    3. Нельзя делать await вызовы из одного потока в другой.

    Поэтому я бы посоветовал такое грубое, но простое решение:
    В одном потоке потоке создавай event loop (сам! это важно!) и запускай бота. В другом потоке создавай и запускай GUI.
    Какой поток должен быть главным - скорее всего не принципиально. Попробуй оба варианта.
    Используй пару очередей (queue.Queue) для синхронизации между потоками.

    Одна очередь будет периодически проверяться в потоке GUI с помощью root.after(). Почитай документацию на этот метод, но если коротко - он позволяет запланировать вызов функции в потоке GUI через время. Эта очередь будет содержать сведения о том, что нужно обновить в GUI.
    Формат и смысл сведений определи сам. Это может быть что-то высокоуровневое в духе "есть сообщение от пользователя такого-то с таким-то текстом", и пусть поток gui сам разбирается, что с этим делать. Это может быть и что-то более низкоуровневое, типа "задай такому-то свойству у такого-то элемента управления такое-то значение". Я бы посоветовал первый вариант - он позволит разделить логику программы чётко на две части.
    Код проверки будет примерно вида
    def check_gui_queue():
        try:
            while True:
                command = gui_queue.get_nowait()  # проверяем, есть ли команда для GUI
                gui_queue.task_done()  # на каждый успешный вызов get() - один вызов task_done()
                pass  # как-то обрабатываем команду
        except queue.Empty:  # команды нет
            root.after(100, check_gui_queue)  # даём GUI поработать спокойно 100 мс

    Разумеется, если ты завернёшь GUI в класс (что имеет смысл) код немного изменится. Но суть останется прежней.

    Другая очередь будет содержать команды для бота. Периодически проверяй её в потоке asyncio с помощью простого кода вида
    async def check_bot_queue():
        while True:
            try:
                command = bot_queue.get_nowait()  # проверяем, есть ли новая команда для бота
            except queue.Empty:
                await asyncio.sleep(0.1)  # нет - даём другим корутинам поработать 100 мс
            else:
                bot_queue.task_done()  # на каждый успешный вызов get() - один вызов task_done()
                pass  # есть - как-то её обрабатываем

    Эту корутину запустишь через create_task(), и она позволит коду GUI организовывать вызовы в коде бота.

    Разумеется, придётся подумать, как аккуратно сделать, чтобы эти две функции не разрослись в дикую простыню из if-elif-else. Но общий принцип примерно такой.
    Время ожидания команды можно увеличить - это замедлит время реакции пре передаче данных между потоками, но уменьшит холостую нагрузку на систему.
    Ответ написан
    Комментировать
  • Как позиционировать виджеты tkinter?

    Vindicar
    @Vindicar
    RTFM!
    Если ты используешь менеджер pack(), используй параметр anchor для выравнивания вдоль другой оси. expand и fill - для заполнения вдоль другой оси.
    Ответ написан
    Комментировать
  • Что не так у меня в коде? Мне в терминале выводит большую ошибку.?

    Vindicar
    @Vindicar
    RTFM!
    Ну так читай внимательно текст ошибки.
    _tkinter.TclError: bad option "-tittle": must be -default, -detail, -icon, -message, -parent, -title, or -type

    Разницу видишь?
    А ошибка в строке
    File "c:\Users\Solo\Desktop\MAXIUMAM2035\Go_user.py", line 19, in button_click
    messagebox.showinfo(tittle='Проверка', message=info_str) # Для вывода информации
                        ^^^^^^
    Ответ написан
    1 комментарий