Feat 8.frontend development #22
218
frontend/api.py
Normal file
218
frontend/api.py
Normal file
@@ -0,0 +1,218 @@
|
||||
from types import SimpleNamespace
|
||||
import urllib
|
||||
import requests
|
||||
|
||||
DEFAULT_URL = "http://127.0.0.1:8000"
|
||||
|
||||
API_TODO_ITEMS_LIST = "api/todo_items/"
|
||||
API_TODO_ITEMS_CREATE = "api/todo_items/"
|
||||
API_TODO_ITEMS_READ = "api/todo_items/{0}/"
|
||||
API_TODO_ITEMS_UPDATE = "api/todo_items/{0}/"
|
||||
API_TODO_ITEMS_PARTIAL_UPDATE = "api/todo_items/{0}/"
|
||||
API_TODO_ITEMS_DELETE = "api/todo_items/{0}/"
|
||||
|
||||
API_LISTS_LIST = "api/lists/"
|
||||
API_LISTS_CREATE = "api/lists/"
|
||||
API_LISTS_READ = "lists/{0}/"
|
||||
API_LISTS_UPDATE = "lists/{0}/"
|
||||
API_LISTS_PARTIAL_UPDATE = "lists/{0}/"
|
||||
API_LISTS_DELETE = "lists/{0}/"
|
||||
|
||||
API_TOKEN = "api/token/"
|
||||
|
||||
|
||||
class UserApi(object):
|
||||
def __init__(self, url=DEFAULT_URL, token=None):
|
||||
"""
|
||||
Constructor
|
||||
|
||||
Parameters
|
||||
----------
|
||||
url : str, optional
|
||||
Server url. The default is DEFAULT_URL.
|
||||
token : dict, optional
|
||||
Existing user tokens to bypass authorization.
|
||||
The default is None.
|
||||
|
||||
Returns
|
||||
-------
|
||||
None.
|
||||
|
||||
"""
|
||||
self.token = token
|
||||
self.get_api = lambda x: urllib.parse.urljoin(url, x)
|
||||
|
||||
# ToDo - store tokens in config
|
||||
def auth(self, user, passwd):
|
||||
"""
|
||||
Authosization
|
||||
|
||||
Parameters
|
||||
----------
|
||||
user : str
|
||||
Login.
|
||||
passwd : str
|
||||
Password.
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict
|
||||
Generated auth token.
|
||||
|
||||
"""
|
||||
token = UserApi._raise_or_return_(
|
||||
requests.post(url=self.get_api(API_TOKEN), json={"username": user, "password": passwd})
|
||||
)
|
||||
self.token = SimpleNamespace(**token)
|
||||
return self.token
|
||||
|
||||
def lists_list(self, **argv):
|
||||
"""
|
||||
List all the exsiting to-do lists.
|
||||
Auth required
|
||||
|
||||
Returns
|
||||
-------
|
||||
list
|
||||
to-do lists.
|
||||
|
||||
"""
|
||||
return UserApi._raise_or_return_(
|
||||
requests.get(
|
||||
url=self.get_api(API_LISTS_LIST), headers=self._access_token_(), params=argv
|
||||
)
|
||||
)
|
||||
|
||||
def lists_create(self, title="Untitled"):
|
||||
"""
|
||||
Create a new to-do list
|
||||
Auth required
|
||||
|
||||
Parameters
|
||||
----------
|
||||
title : str, optional
|
||||
New list name. The default is "Untitled".
|
||||
|
||||
"""
|
||||
return UserApi._raise_or_return_(
|
||||
requests.post(
|
||||
url=self.get_api(API_LISTS_CREATE),
|
||||
json={"title": title},
|
||||
headers=self._access_token_(),
|
||||
)
|
||||
)
|
||||
|
||||
def todo_items_list(self, **argv):
|
||||
"""
|
||||
List all the exsiting to-do items.
|
||||
Auth required
|
||||
|
||||
Returns
|
||||
-------
|
||||
list
|
||||
to-do items.
|
||||
|
||||
"""
|
||||
return UserApi._raise_or_return_(
|
||||
requests.get(
|
||||
url=self.get_api(API_TODO_ITEMS_LIST), headers=self._access_token_(), params=argv
|
||||
)
|
||||
)
|
||||
|
||||
# def create(self, title="Untitled"):
|
||||
# """
|
||||
# Create a new to-do list
|
||||
# Auth required
|
||||
|
||||
# Parameters
|
||||
# ----------
|
||||
# title : str, optional
|
||||
# New list name. The default is "Untitled".
|
||||
|
||||
# """
|
||||
# response = requests.post(
|
||||
# url=self.get_api(API_LISTS_CREATE), json={"title": title}, headers=self._access_token_()
|
||||
# url=self.get_api(API_TODO_ITEMS_CREATE), json={"title": title}, headers=self._access_token_()
|
||||
# )
|
||||
# response.raise_for_status()
|
||||
# return response.json()
|
||||
|
||||
# def read(self, id):
|
||||
# """
|
||||
# Read a to-do list contents
|
||||
|
||||
# Parameters
|
||||
# ----------
|
||||
# id : int
|
||||
# List id.
|
||||
|
||||
# Returns
|
||||
# -------
|
||||
# list
|
||||
# Requested contents
|
||||
|
||||
# """
|
||||
# response = requests.post(
|
||||
# url=self.get_api(API_LISTS_READ).format(id), headers=self._access_token_()
|
||||
# )
|
||||
# response.raise_for_status()
|
||||
# return response.json()
|
||||
|
||||
# def update(self, id, title="Untitled"):
|
||||
# """
|
||||
# Add a to-do item to the list
|
||||
|
||||
# Parameters
|
||||
# ----------
|
||||
# id : int
|
||||
# List id.
|
||||
# title : str, optional
|
||||
# To-do item title. The default is "Untitled".
|
||||
|
||||
# """
|
||||
# response = requests.put(
|
||||
# json={"title": title},
|
||||
# url=self.get_api(API_LISTS_UPDATE).format(id),
|
||||
# headers=self._access_token_(),
|
||||
# )
|
||||
# response.raise_for_status()
|
||||
# return response.json()
|
||||
|
||||
# def partial_update(self, id, title="Untitled"):
|
||||
# """
|
||||
# Update list item - untrusted
|
||||
|
||||
# """
|
||||
# response = requests.patch(
|
||||
# json={"title": title},
|
||||
# url=self.get_api(API_LISTS_PARTIAL_UPDATE).format(id),
|
||||
# headers=self._access_token_(),
|
||||
# )
|
||||
# response.raise_for_status()
|
||||
# return response.json()
|
||||
|
||||
# def delete(self, id):
|
||||
# """
|
||||
# Delete list
|
||||
|
||||
# Parameters
|
||||
# ----------
|
||||
# id : int
|
||||
# List id to delete.
|
||||
|
||||
# """
|
||||
# response = requests.delete(
|
||||
# url=self.get_api(API_LISTS_DELETE).format(id), headers=self._access_token_()
|
||||
# )
|
||||
# response.raise_for_status()
|
||||
# return response.json()
|
||||
|
||||
def _access_token_(self):
|
||||
if self.token is None:
|
||||
raise RuntimeError("Authosization required for requested operation!")
|
||||
return {"Authorization": f"Bearer {self.token.access}"}
|
||||
|
||||
@staticmethod
|
||||
def _raise_or_return_(response):
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
48
frontend/api_demo.py
Normal file
48
frontend/api_demo.py
Normal file
@@ -0,0 +1,48 @@
|
||||
from user import User
|
||||
|
||||
|
||||
def print_lists(lists):
|
||||
for item in lists:
|
||||
print(f"List: '{item}'", f"Id: {item.id}", "|", "|".join([str(x) for x in item.items]))
|
||||
|
||||
|
||||
DEFAULT_URL = "http://127.0.0.1:8000"
|
||||
|
||||
user = User(url=DEFAULT_URL)
|
||||
user.auth("root", "root")
|
||||
|
||||
# Fetch existing lists:
|
||||
lists = user.fetchUserLists()
|
||||
print("Fecthing...")
|
||||
print_lists(lists)
|
||||
|
||||
# Remove user list by id:
|
||||
user.removeUserList(5)
|
||||
lists = user.fetchUserLists()
|
||||
print(f"Removing {5}...")
|
||||
print_lists(lists)
|
||||
|
||||
# Append a new list to user:
|
||||
print("Appending list...")
|
||||
scroll = user.appendUserList(title="a new list!")
|
||||
print_lists(lists)
|
||||
|
||||
# Modify list 0:
|
||||
print("Modifyng list...")
|
||||
lists[0].modify(title="A new title")
|
||||
print_lists(lists)
|
||||
|
||||
# Append item to list:
|
||||
print("Appending item to last list...")
|
||||
item = lists[-1].append(text="this is an item")
|
||||
print_lists(lists)
|
||||
|
||||
# Modifying item
|
||||
print("Modifyng appended item...")
|
||||
item.modify(finished=True, text="this is an updated item")
|
||||
print_lists(lists)
|
||||
|
||||
# Removing item at 0
|
||||
print("Removing item 0 from list 0...")
|
||||
lists[0].remove(0)
|
||||
print_lists(lists)
|
||||
68
frontend/login.py
Normal file
68
frontend/login.py
Normal file
@@ -0,0 +1,68 @@
|
||||
import tkinter as tk
|
||||
from user import User
|
||||
import message
|
||||
|
||||
|
||||
class LoginFrame(tk.Frame):
|
||||
|
||||
loggedIn = False
|
||||
|
||||
def __init__(self, master=None, url=None) -> None:
|
||||
"""
|
||||
Функция инициаизации класса
|
||||
|
|
||||
"""
|
||||
super().__init__(master)
|
||||
|
||||
# Иницализируем параметры окна
|
||||
self.master = master
|
||||
self.pack(fill=tk.BOTH, expand=1)
|
||||
|
||||
# self.grid(row=0, column=0, sticky=tk.N + tk.S + tk.E + tk.W)
|
||||
# tk.Grid.rowconfigure(master, 0, weight=1)
|
||||
# tk.Grid.columnconfigure(master, 0, weight=1)
|
||||
|
||||
# Иницализируем параметры пользователя
|
||||
self.user = User(url=url)
|
||||
|
||||
# Настраиваем размеры и включаем иницализацию
|
||||
self.initAUTH()
|
||||
|
||||
def login_clicked(self) -> None:
|
||||
"""
|
||||
Функция авторизации
|
||||
"""
|
||||
try:
|
||||
self.user.auth(self.login.get(), self.password.get())
|
||||
self.loggedIn = True
|
||||
except Exception as ex:
|
||||
print(ex)
|
||||
message.invalid_login()
|
||||
|
||||
def initAUTH(self) -> None:
|
||||
"""
|
||||
Создает окно авторизации программы
|
||||
"""
|
||||
# Конфигурируем сетку
|
||||
for rows in range(25):
|
||||
tk.Grid.rowconfigure(self, rows, weight=1)
|
||||
|
||||
for columns in range(25):
|
||||
tk.Grid.columnconfigure(self, columns, weight=1)
|
||||
|
||||
# Подпись и поле ввода для логина
|
||||
login_label = tk.Label(self, text="Введите логин")
|
||||
login_label.grid(row=9, column=12, columnspan=3, rowspan=1, sticky="nsew")
|
||||
|
||||
self.login = tk.Entry(self)
|
||||
self.login.grid(row=10, column=12, columnspan=3, rowspan=1, sticky="nsew")
|
||||
|
||||
# Подпись и поле ввода для пароля
|
||||
password_label = tk.Label(self, text="Введите пароль")
|
||||
password_label.grid(row=11, column=12, columnspan=3, rowspan=1, sticky="nsew")
|
||||
|
||||
self.password = tk.Entry(self, show="*")
|
||||
self.password.grid(row=12, column=12, columnspan=3, rowspan=1, sticky="nsew")
|
||||
|
||||
# Кнопка авториазции
|
||||
btn = tk.Button(self, text="Войти", command=self.login_clicked)
|
||||
btn.grid(row=14, column=12, columnspan=3, rowspan=1, sticky="nsew")
|
||||
20
frontend/message.py
Normal file
20
frontend/message.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from tkinter import messagebox as mb
|
||||
|
||||
TITLE_INFO_BOX = "Сообщение!"
|
||||
MESSAGE_INVALID_LOGIN = "Неправильный логин или пароль"
|
||||
MESSAGE_EMPTY = "Сдесь могло быть ваше сообщение"
|
||||
|
||||
|
||||
def infobox(msg: str = None) -> None:
|
||||
"""
|
||||
Показывает передаваемое сообщение в messagebox
|
||||
|
||||
:param msg: передаваемое сообщение
|
||||
"""
|
||||
if msg is None:
|
||||
msg = MESSAGE_EMPTY
|
||||
mb.showinfo(TITLE_INFO_BOX, msg)
|
||||
|
||||
|
||||
def invalid_login():
|
||||
infobox(MESSAGE_INVALID_LOGIN)
|
||||
@@ -1,14 +1,55 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import tkinter as tk
|
||||
from login import LoginFrame
|
||||
from workspace import WorkSpaceFrame
|
||||
|
||||
if "win" in sys.platform.lower():
|
||||
DEFAULT_URL = "http://localhost:8000"
|
||||
else:
|
||||
DEFAULT_URL = "http://0.0.0.0:8000"
|
||||
|
||||
BASE_W = 580
|
||||
BASE_H = 400
|
||||
|
||||
TITLE_APP = "ToDo Application"
|
||||
|
||||
|
||||
class Application(tk.Frame):
|
||||
def __init__(self, master=None):
|
||||
super().__init__(master)
|
||||
class Application(tk.Tk):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.center_window()
|
||||
self.title(TITLE_APP)
|
||||
|
||||
def login(self):
|
||||
"""Возвращает пользователя - его можно потом сериализовать"""
|
||||
self.frame = LoginFrame(master=self, url=DEFAULT_URL)
|
||||
while not self.frame.loggedIn:
|
||||
|
Возможно, полезно было бы сохранять JWT токен в отдельный файл, чтобы логиниться только когда токен протухнет Возможно, полезно было бы сохранять JWT токен в отдельный файл, чтобы логиниться только когда токен протухнет
Я там в одном из комментов предлагал сделать юзера json-сериализуемым Я там в одном из комментов предлагал сделать юзера json-сериализуемым
Зачем его сериализовать? Достаточно сохранить просто токен и добавить загрузку этого токена при запуске. Зачем его сериализовать? Достаточно сохранить просто токен и добавить загрузку этого токена при запуске.
Логин\пароль и другие данные не нужны, их можно подгружать.
Ну пл сути все что будет в сериализации это пара токенов и GUI будет известен только юзер Ну пл сути все что будет в сериализации это пара токенов и GUI будет известен только юзер
|
||||
self.update_idletasks()
|
||||
self.update()
|
||||
self.frame.destroy()
|
||||
return self.frame.user
|
||||
|
||||
def main(self, user):
|
||||
self.frame = WorkSpaceFrame(master=self, user=user)
|
||||
self.mainloop()
|
||||
|
||||
def center_window(self, width: str = BASE_W, heigh: str = BASE_H) -> None:
|
||||
"""
|
||||
Центрирует приложение по центру экрана
|
||||
|
||||
:param width: ширина окна
|
||||
:param heigh: высота окна
|
||||
"""
|
||||
sw = self.winfo_screenwidth()
|
||||
sh = self.winfo_screenheight()
|
||||
|
||||
x = (sw - width) / 2
|
||||
y = (sh - heigh) / 2
|
||||
self.geometry("%dx%d+%d+%d" % (width, heigh, x, y))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = Application()
|
||||
app.master.title("ToDo")
|
||||
app.mainloop()
|
||||
app.main(app.login())
|
||||
|
||||
108
frontend/user.py
Normal file
108
frontend/user.py
Normal file
@@ -0,0 +1,108 @@
|
||||
import os
|
||||
|
||||
from datetime import datetime
|
||||
from api import UserApi
|
||||
|
||||
|
||||
class ToDoList(object):
|
||||
def __init__(self, id, title, created_at=None, items=[], parent=None):
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.items = items
|
||||
self.created_at = created_at
|
||||
|
||||
def __iter__(self):
|
||||
for item in self.items:
|
||||
yield item
|
||||
|
||||
def __getitem__(self, index):
|
||||
return self.items[index]
|
||||
|
||||
def __len__(self):
|
||||
return len(self.items)
|
||||
|
||||
def __str__(self):
|
||||
return f"[{self.id}] {self.title}"
|
||||
|
||||
def index(self, value):
|
||||
return self.items.index(value)
|
||||
|
||||
# ToDo
|
||||
def remove(self, index):
|
||||
self.items.remove(self.items[index])
|
||||
self.sync()
|
||||
|
||||
# ToDo
|
||||
def append(self, text):
|
||||
item = ToDoItem(id=None, text=text, created_at=datetime.now())
|
||||
self.items.append(item)
|
||||
item.sync()
|
||||
self.sync()
|
||||
return item
|
||||
|
||||
def modify(self, **argv):
|
||||
for key, value in argv.items():
|
||||
setattr(self, key, value)
|
||||
self.sync()
|
||||
|
||||
# ToDo
|
||||
def sync(self):
|
||||
# ToDo send request or store in form
|
||||
print(f"Item '{self}' is being synchronized...")
|
||||
|
||||
|
||||
class ToDoItem(object):
|
||||
def __init__(self, id, text, finished=False, created_at=None, parent=None):
|
||||
self.id = id
|
||||
self.text = text
|
||||
self.finished = finished
|
||||
self.created_at = created_at
|
||||
|
||||
def __str__(self):
|
||||
return f"[{self.id}] {self.text}"
|
||||
|
||||
def modify(self, **argv):
|
||||
for key, value in argv.items():
|
||||
setattr(self, key, value)
|
||||
self.sync()
|
||||
|
||||
# ToDo
|
||||
def sync(self):
|
||||
# ToDo send request or store in form
|
||||
print(f"Item '{self}' is being synchronized...")
|
||||
|
||||
|
||||
class User(UserApi):
|
||||
def auth(self, user, passwd):
|
||||
if "DEBUG" in os.environ:
|
||||
return
|
||||
UserApi.auth(self, user, passwd)
|
||||
|
||||
# ToDo
|
||||
items = [
|
||||
ToDoList(
|
||||
id=i,
|
||||
title=f"List {i}",
|
||||
created_at=datetime.now(),
|
||||
items=[
|
||||
ToDoItem(id=i * 10 + j, text=f"Item {i*10+j}", created_at=datetime.now())
|
||||
for j in range(10)
|
||||
],
|
||||
)
|
||||
for i in range(10)
|
||||
]
|
||||
|
||||
# ToDo
|
||||
def fetchUserLists(self):
|
||||
return self.items
|
||||
|
||||
# ToDo
|
||||
def removeUserList(self, id):
|
||||
self.items = [item for item in self.items if item.id != id]
|
||||
|
||||
# ToDo
|
||||
|
Непонятно, что делает ToDo. Если это маркер для того, что надо сделать, то лучше писать, что именно сделать Непонятно, что делает ToDo. Если это маркер для того, что надо сделать, то лучше писать, что именно сделать
Реализовать ¯\(ツ)/¯ Реализовать ¯\\_(ツ)_/¯
|
||||
def appendUserList(self, title):
|
||||
item = ToDoList(id=None, title=title, created_at=datetime.now())
|
||||
self.items.append(item)
|
||||
item.sync()
|
||||
return item
|
||||
163
frontend/workspace.py
Normal file
163
frontend/workspace.py
Normal file
@@ -0,0 +1,163 @@
|
||||
import tkinter as tk
|
||||
|
||||
|
||||
def str_time(time):
|
||||
return time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
|
||||
TODO_ITEM_TABLE_TEXT_WIDTH = 15
|
||||
TODO_ITEM_TABLE_FINISHED_WIDTH = 8
|
||||
TODO_ITEM_TABLE_CREATED_AT_WIDTH = 15
|
||||
|
||||
|
||||
def placeholder():
|
||||
print("Не реализовано")
|
||||
|
||||
|
||||
class ToDoItemWidget(tk.Frame):
|
||||
@staticmethod
|
||||
def header(parent):
|
||||
body = tk.Frame(parent)
|
||||
|
||||
text = tk.Label(body, text="Текст", width=TODO_ITEM_TABLE_TEXT_WIDTH)
|
||||
text.pack(side="left")
|
||||
|
||||
text = tk.Label(body, text="Выполнено", width=TODO_ITEM_TABLE_FINISHED_WIDTH)
|
||||
text.pack(side="left")
|
||||
|
||||
text = tk.Label(body, text="Создано", width=TODO_ITEM_TABLE_CREATED_AT_WIDTH)
|
||||
text.pack(side="left")
|
||||
|
||||
return body
|
||||
|
||||
def __init__(self, *args, item, **argv):
|
||||
super().__init__(*args, **argv)
|
||||
|
||||
self.parent = self.master
|
||||
self.item = item
|
||||
|
||||
self.noteLabel = tk.Label(self, text=item.text, width=TODO_ITEM_TABLE_TEXT_WIDTH)
|
||||
self.noteLabel.pack(side="left")
|
||||
|
||||
self.finished = tk.IntVar(value=int(item.finished))
|
||||
self.finishedButton = tk.Checkbutton(
|
||||
self,
|
||||
variable=self.finished,
|
||||
command=self.finishedButton_command,
|
||||
width=TODO_ITEM_TABLE_FINISHED_WIDTH,
|
||||
)
|
||||
self.finishedButton.pack(side="left")
|
||||
|
||||
self.createdAt = tk.Label(
|
||||
self, text=str_time(item.created_at), width=TODO_ITEM_TABLE_CREATED_AT_WIDTH
|
||||
)
|
||||
self.createdAt.pack(side="left")
|
||||
|
||||
self.remove = tk.Button(self, text="Удалить", command=lambda: self.parent.remove(self.item))
|
||||
self.remove.pack(side="left")
|
||||
|
||||
def finishedButton_command(self):
|
||||
self.item.modify(finished=self.finished.get() > 0)
|
||||
|
||||
|
||||
class ToDoListWidget(tk.Frame):
|
||||
def __init__(self, *args, **argv):
|
||||
super().__init__(*args, **argv)
|
||||
|
||||
def fill(self, itemList):
|
||||
|
||||
header = ToDoItemWidget.header(self)
|
||||
header.pack(side="left")
|
||||
header.pack(side="top", fill="y")
|
||||
|
||||
self.itemList = itemList
|
||||
|
||||
for item in itemList:
|
||||
item = ToDoItemWidget(self, item=item)
|
||||
item.pack(side="top", fill="y")
|
||||
|
||||
self.itemToAdd = tk.Text(self, width=15, height=1)
|
||||
self.itemToAdd.pack(side="top")
|
||||
|
||||
add = tk.Button(self, text="Добавить заметку", command=self.add_command)
|
||||
add.pack(side="top")
|
||||
|
||||
delete = tk.Button(self, text="Удалить лист", command=placeholder)
|
||||
delete.pack(side="top")
|
||||
|
||||
def update(self, itemList=None):
|
||||
self.clear()
|
||||
if itemList is None:
|
||||
self.fill(self.itemList)
|
||||
else:
|
||||
self.fill(itemList)
|
||||
|
||||
def add_command(self):
|
||||
self.itemList.append(self.itemToAdd.get(1.0, "end"))
|
||||
self.update()
|
||||
|
||||
def remove(self, item):
|
||||
self.itemList.remove(self.itemList.index(item))
|
||||
self.update()
|
||||
|
||||
def clear(self):
|
||||
for widget in self.winfo_children():
|
||||
widget.destroy()
|
||||
|
||||
|
||||
class WorkSpaceFrame(tk.Frame):
|
||||
def __init__(self, user, master=None, url=None) -> None:
|
||||
"""
|
||||
Функция инициаизации класса
|
||||
"""
|
||||
super().__init__(master)
|
||||
|
||||
self.master = master
|
||||
self.user = user
|
||||
|
||||
self.pack(fill=tk.BOTH, expand=1)
|
||||
self.initLayout(user)
|
||||
|
||||
def initLayout(self, user):
|
||||
|
||||
# data
|
||||
self.lists = user.fetchUserLists()
|
||||
|
||||
text = tk.Text(self, width=15, height=1)
|
||||
text.pack(anchor="sw")
|
||||
|
||||
add = tk.Button(self, text="Добавить лист", command=placeholder)
|
||||
add.pack(anchor="sw")
|
||||
|
||||
# select list box
|
||||
self.listBox = tk.Listbox(self, width=30, selectmode=tk.SINGLE)
|
||||
self.listBox.pack(side="left", fill="y")
|
||||
self.listBox.bind("<<ListboxSelect>>", self.listBox_selected)
|
||||
|
||||
# scroll bar
|
||||
scrollbar = tk.Scrollbar(self, orient="vertical")
|
||||
scrollbar.config(command=self.listBox.yview)
|
||||
scrollbar.pack(side="left", fill="y")
|
||||
|
||||
# add scroll bar to list box
|
||||
self.listBox.config(yscrollcommand=scrollbar.set)
|
||||
|
||||
# fill list box
|
||||
for item in self.lists:
|
||||
s = f"{str(item)}: {item.created_at.strftime('%Y-%m-%d %H:%M:%S')}"
|
||||
self.listBox.insert(tk.END, s)
|
||||
self.listBox.pack()
|
||||
len(self.lists) > 0 and self.listBox.selection_set(first=0)
|
||||
|
||||
# todo lists
|
||||
self.toToList = ToDoListWidget(self)
|
||||
self.toToList.pack(side="left", fill="both", expand=1)
|
||||
|
||||
def listBox_selected(self, *args):
|
||||
|
||||
self.toToList.clear()
|
||||
|
||||
selection = self.listBox.curselection()
|
||||
cur = selection[0]
|
||||
|
||||
self.toToList.fill(self.lists[cur])
|
||||
Reference in New Issue
Block a user
Такой комментарий очень похож на очевидный. Очевидные комментари лучше не вставлять, т.к. они только затрудняют чтение
Осталось от кода Александра