LINUX.ORG.RU

Замещение главного окна в tkinter

 , ,


0

2

Всем привет.

Есть программа (уже писал в соседних тредах, но выведу в новый тред, так как новый вопрос ближе к tkinter всё-таки). Чат клиент-серверный. Хочу написать GUI к клиенту. (желательно использовать именно tkinter, но если так и не разберусь, плюну и буду пытаться освоить pyqt4). Ну да не в тему.

В общем, хочу, чтобы самое первое окно при запуске программы спрашивало адрес сервера + порт сервера - с единственной кнопочкой connect. После нажатия на которую (в случае успешного соединения, ошибку разберусь как лучше обработать) - старое окно как бы удалялось, и замещалось новым - где уже требовалось бы ввести login/password. После авторизации надо чтобы второе окно тоже пропадало - и уже появлялось окно собственно чата (список комнат), и уже оно становилось бы главным.

Как такое реализовать (и можно ли)? Мне нужен TopLevel() или что-то другое?

Главное окно создаю примерно так:

root = tkinter.Tk()
root.title('blah-blah-blah')
# много кода для всяких виджетов + обработка нажатия на кнопку
# в качестве родителя указываю root
root.mainloop()

Собственно метод нажатия на кнопку (частично)

newroot = tkinter.TopLevel()
#опять куча виджетов, теперь у них родитель newroot

Собственно, создаётся новое окно, а старое не убирается... и старое остаётся главным (если закрыть первое, закроется и второе). Сделать второе главным? (а после успешного логина сделать третьё)?..

Собственно, если кто подскажет какую схему с pyqt4 провернуть (аналогичную - тоже благодарен)

BattleCoder ★★★★★
() автор топика
Ответ на: комментарий от power

ок... попробую (то есть в случае pyqt4 просто метод show() не вызывать). Как вариант...

или как вариант сразу создавать окно со списком комнат (но показывать его не сразу)

попробую.

BattleCoder ★★★★★
() автор топика

У окна, если не ошибаюсь, есть метод root.withdraw(), который его прячет.

anonymous
()

Тебе не TopLevel(), он по сути просто создает еще одно активное окно (именно окно, а не диалог), которое не блокирует родительское. Но оно все равно остается дочерним.

А почему не сделать проще - сделать два состояния у родительского окна. При первом несколько entry + кнопки. Коннет успешен - старые виджеты просто удаляются с фрейма, а добавляются новые, котороые собственно и необходимы. При этом весь список виджетов с параметрами можно держать либо в словарях, либо в списках. Выстраивание интерфейса соответственно через обработчик списка/словаря. Ну собственно и получаем профит :).

i_overdose
()
Ответ на: комментарий от i_overdose

З.Ы.: Может коряво описал, но примерно такой принцип у меня использует клиент, который сейчас к серваку на python как раз пишу. Только я брал сразу Tcl/Tk, на нем такой подход ИМХО логичнее. Для python...можно по идее состояния попробовать через объект реализовать. Будут «те же яйца только в профиль».

i_overdose
()
Ответ на: комментарий от i_overdose

приходила такая мысль в голову, почему-то подумал, что так неправильно...

ладно, сейчас с pyqt4 шаманю, будет время - попробую такую схему :)

BattleCoder ★★★★★
() автор топика
Ответ на: комментарий от BattleCoder

Такая схема как раз является Ъ-Tk (на сколько я знаю). Я сейчас даже процедуры для кнопкокоманд собираю в зависимости от состояния. Получается, ИМХО, удобно. Хотя определенная порция веществ в данном подходе все же есть. Но тут... как говорится на вкус и цвет все фломастеры разные.

З.Ы.: сорри за вопрос, но все же - а почему не родной tcl/tk? тогда работать будет на любом утюге, где есть виртуальная машина тикля. Для задачи - подходит на первый взгляд почти идеально.

i_overdose
()
Ответ на: комментарий от i_overdose

цель задачи вообще говоря изучение питона...

впрочем tkinter сюда даже больше подходит (так как входит в стандартные библиотеки). просто pyqt4 просто приятнее... да и не такая проблема его установить.

а на утюге мне эта штука точно не нужна :)

BattleCoder ★★★★★
() автор топика
import Tkinter

def mainWindow1(*w,**kw):
    root = Tkinter.Tk()
    root.title('window 1')
    def butCallback():
        root.destroy()
        mainWindow2()

    but1 = Tkinter.Button(root,
                        text = "-> window 2",
                        command = butCallback )    
    but1.pack()
    root.mainloop()
    
def mainWindow2(*w,**kw):
    root = Tkinter.Tk()
    root.title('window 2')
    def butCallback():
        root.destroy()
        mainWindow1()

    but1 = Tkinter.Button(root,
                        text ="-> window 1",
                        command = butCallback )    
    but1.pack()  
    text1 = Tkinter.Text(root)    
    text1.pack()


    root.mainloop()
    
if __name__ == '__main__':
    mainWindow1()
sp3
()
Ответ на: комментарий от BattleCoder

Тогда точно лучше Tkinter. Судя по словам Гвидо для питона он Ъ и наиболее удобный/готовый фреймворк. Ну и позволяет писать на «правильном» питоновом стиле (pythonic по моему это обзывается, хотя могу ошибаться).

i_overdose
()
Ответ на: комментарий от sp3

Это конечно классно, но тут получается «создание» одинаковых сущностей (получается что начало создается один цикл Tk, потом он после манипуляций уничтожается вместе с окном, потом опять создается цикл Tk. Для Tk (и tkinter) правильнее сделать все в одном цикле через пересборку виджетов на рутовом окне.

i_overdose
()
Ответ на: комментарий от i_overdose

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

BattleCoder ★★★★★
() автор топика
Ответ на: комментарий от BattleCoder

Тогда советую приглядеться к Tcl (мощный язык, мне больше чем lisp понравился - простой, гибкий, с понятным читаемым синтаксисом) или Ruby (раньше на нем писал, остались только лучшие воспоминания и чувства.

А Python самого разочаровал. Не хватает мне в нем гибкости tcl (чем больше общаюсь с CommonLisp и Tcl тем больше мне нравится «тикль» [раньше было наоборот]), читаемого синтаксиса (недавно доставал исходник написанный на tcl года два назад - вспомнил очень быстро). Ну и поддержка парадигм в Tcl гораздо шире чем в Python. «Тикль» будет таким как хочешь ты, а python заставит тебя перестроиться под него. В этом их разница.

i_overdose
()
Ответ на: комментарий от i_overdose

Ладно. Тогда ответьте на такой вопрос... как правильно лучше сделать.

Вот есть чат-клиент. Сетевое приложение. Суть заключается в том, что обращается к удалённому серверу (через сокет), в главном потоке посылает команды, в другом потоке принимает ответы и уведомления. (так как sock.recv() - операция блокирующая, выделил в отдельный поток).

Чтобы не блокировать интерфейс при отправке команд (когда нужно получить ответ), например, при отправке сообщения делаю похожий запрос (json):

{'method': 'message', 'params': [ключ, комната, сообщение], id:1234}

в ответ должен получить

{'result': 'ok', id:1234}, либо {'error': [код ошибки, сообщение об ошибке], id:1234}

Ждать в потоке GUI когда придёт ответ - я не могу... но мне надо этот ответ как-то обработать... и после этого в gui потоке сделать изменения (например, вывести новое сообщение в окно чата. В случае «уведомлений» мне надо, например, добавить нового пользователя в чат (или удалить того, кто разлогинился).

Как мне такое реализовать? на tkinter. Чтобы GUI не блокировался.

BattleCoder ★★★★★
() автор топика
Ответ на: комментарий от BattleCoder

А зачем делать блокировку гуя в tkinter? Если ты выстраиваешь его динамически, то достаточно держать все элементы интерфейса в списках/словарях (удобнее ИМХО словари). Т.е. ты делаешь рутовое окно, на нем фрейм. А во фрейм все выстраивается динамически через обработку словаря. Соответственно клиент принимает любые запросы, которые меняют списки/словари по которым выстраивается интерфейс, а интерфейс в свою очередь просто работает - список изменился - выстроился «новый интерфейс».

В tcl я так сделал на основе как раз словарей - т.е. есть в клиенте функция которая постоянно принимает ответы и обрабатывает их (разносит по спискам) и меняет запросы она выполняется первой в списке команд цикла Tk (до того как на фрейм выстраиваются все виджеты). В свою очередь по словарям выстраивается вообще все в программе - начиная от интерфейса программы и заканчивая функциями для виджетов (command, которые). Сам цикл Тк выполняется до тех пор пока существует рутовое окно, к которому оно привязано (т.е. root=Tkinter Tk()). Соответственно все что между Tk() и mainloop() будет выполнятся постоянно, пока окно не будет уничтожено (в том числе и функция, которая тебе нужна).

(как подсказывает мне мой коллега, который как раз пишет сервак на питоне - он бы сделал просто два потока, в одном бы крутил сам GUI, а во второй бы запихал всю работу с сетью, а потоки бы обменивались между собой данными при необходимости [но у него есть нехорошая привычка все распаралеливать, так что решение, ИМХО, спорное])

i_overdose
()
Ответ на: комментарий от i_overdose

первый способ так и не понял. можно на простом примере?

со вторым вроде понятно... только из главного потока управления надо будет по таймеру запускать обновление..

BattleCoder ★★★★★
() автор топика
Ответ на: комментарий от i_overdose

сделал в общем. вторым способом. в целом работает.

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

Используется метод .after()

BattleCoder ★★★★★
() автор топика
Ответ на: комментарий от i_overdose

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

наверное на qt лучше писать всё-таки используя C++

BattleCoder ★★★★★
() автор топика
Ответ на: комментарий от BattleCoder

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

А почему «в целом»?

i_overdose
()
Ответ на: комментарий от BattleCoder

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

Понимаю, так же. Сначала смотрел серьезно в сторону Qt. Но выбрал Tk. Ни разу не пожалел.

наверное на qt лучше писать всё-таки используя C++

Может быть :), я сам на Qt+C++ ничего не писал. Только qt+ruby. Тоже не понравилось.

i_overdose
()
Ответ на: комментарий от sp3

А зачем пересоздавать цикл Tk, если можно обойтись без этого? Предложенный Вами подход загромождает исходник лишними сущностями и немного не соответствует концепции tkinter. Последний ведь уходит корнями в tcl, а для него подход создал/уничтожил не свойственный (ведь проще изменить элемент списка, чем переписать заново целый интерфейс).

i_overdose
()
Ответ на: комментарий от i_overdose

ну... gui работают. проблемы там в другом... в самой реализации, в «ядре» так сказать приложения, в его логике.

И ещё мне не нравится что все методы, на которые я навесил реакции по событиям - находятся в классе, который MainForm (притом, что форм несколько).... по-хорошему выделить надо было в отдельный класс, да и в целом привести код в порядок. Но я ограничен во времени. И вообще, IMHO, задача для питона не очень :) буду пробовать сделать аналогичное на других ЯП (смотрю в сторону scala и erlang) - посмотрю, что получится...

хотя может и ошибаюсь. И можно просто пересмотреть архитектуру... и на питоне тоже в общем-то код будет неплохим и вонять не будет. :)

BattleCoder ★★★★★
() автор топика
Ответ на: комментарий от i_overdose

Может быть :), я сам на Qt+C++ ничего не писал.

Я писал. Немного. В целом qt библиотека «ближе» именно к C++, по своим принципам что ли. И больше на этот ЯП ложится. Хотя писать можно на многих ЯП, да... И, кроме того, хорошо его дополняет (помимо gui там есть коллекции, сигналы-слоты, треды, sql и т.п... использовал из этого далеко не всё, и, так сказать, «не распробовал»).

просто вот например в питоне те же коллекции из qt явно лишние и смотрятся чужеродно... имеет смысл применять только qt-core (частично) и qt-gui (основное).

BattleCoder ★★★★★
() автор топика
Ответ на: комментарий от BattleCoder

ИМХО задача для erlang или scala не совсем подходящая, слишком мелкая. Не увидите «профита» от данных языков (а вот сервер на erlang сделать - в самый раз). Если хотите что-то с более красивой архитектурой, сто стоит скорее взять tcl/tk (ИМХО), так как вы на нем сможете написать очень интересный и удобный код, который будет меняться как пластилин под решаемую задачу. Ну и от классов избавиться можно будет (там они просто не нужны).

P.S.: а так, питон не самый лучший язык ИМХО для «быстрого написания» (как не парадоксально это ни звучит), слишком в нем много тонкостей и подводных камней, которые сразу, при «быстрой разработке» и не увидеть. Тем более как ООП-язык он очень неоднозначен. Ruby тот же - лучше объективно для этого (можно еще Lua, Io, OpenEuphoira). К примеру мой коллега сейчас пишет на python3 сервер. Так написав его ядро он понял, что надо было по другому. А лучше на erlang или tcl.

i_overdose
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.