turns-00052.parquet:28634
c697a1b02d1cf4fbd8edb68bdegenerate_repetitionAbsentFinal dense release
Select one behavior. Every returned turn has one binary label: Present or Absent. Source: final dense boolean release.
c697a1b02d1cf4fbd8edb68bWho invented the telescope
The invention of the telescope is generally credited to Hans Lippershey, a Dutch spectacle maker, who applied for the first known patent for a telescope in 1608. However, there is some evidence that other Dutch opticians like Jacob Metius and Zacharias Janssen may have independently created similar devices around the same time. The use of the telescope for astronomical purposes was popularized by Galileo Galilei, who made significant improvements to its design and used it to make important astronomical discoveries in 1609.
b28ddf7b5df9cb2256c18028Запрос: Создай пары вопросов-ответов, описывающих термины статьи. Вопрос должен начинаться с "Что такое". В ответе должно быть описание детальное термина из вопроса. Примеры вопросов: "Что такое план счетов?", "Что такое конфигуратор?", "Что такое справочник?". В ответах используй только информацию из статьи. Вопросы и ответы должны иметь законченный и понятный смысл. Вопросов и ответов должно быть немного, они должны быть максимально детальными и развернутыми, не делай кратких вопросов-ответов. Текст должен быть самодостаточным. Не используй выражение "в данном контексте". Включай в ответ символы перевода строки там, где это требуется по смыслу. Включай упоминание "1С:Бухгалтерия 8" (ред. 3.0) в каждый вопрос и ответ там, где это уместно. Формат ответа на запрос должен быть в виде текста MarkDown: [Вопрос] Текст вопроса? [/Вопрос] [Ответ] Текст ответа [/Ответ] ------------------------------ [Вопрос] Текст вопроса? [/Вопрос] [Ответ] Текст ответа [/Ответ] ------------------------------ Текст статьи: Где указать способ отражения расходов на оплату труда для организации в "1С:Бухгалтерии 8" (ред. 3.0)? ?Способ отражения расходов на оплату труда для организации указывается в настройке учета зарплаты: 1. Раздел Зарплата и кадры - Настройки зарплаты - ссылка "Порядок учета зарплаты" . 2. На закладке "Зарплата" в разделе "Отражение в учете зарплаты" в поле "Способ отражения в бух.учете" указывается способ отражения начислений в учете для организации в целом. Значение выбирается из справочника "Способы учета зарплаты" (раздел Зарплата и кадры - Настройки зарплаты - раздел "Отражение в учете" - ссылка "Способы учета зарплаты"). Предопределенным способом отражения расходов на оплату труда в программе является "Отражение начислений по умолчанию". Этот способ настроен в программе таким образом, чтобы по умолчанию отражать начисления проводкой по дебету счета 26 "Общехозяйственные расходы" и кредиту счета 70 "Расчеты с персоналом по оплате труда". Если необходимый способ отражения для организации в справочнике "Способы учета зарплаты" отсутствует, то его необходимо создать. Для каждого способа указывается только счет дебета (счет учета расходов по заработной плате) и аналитика бухгалтерской проводки по начислению сотрудникам заработной платы. Корреспондирующим счетом этой проводки всегда является счет 70. 3. В поле "Действует с" укажите период начиная с которого будет действовать указанный способ отражения. 4. Историю изменений отражения зарплаты в бухгалтерском учете можно посмотреть, нажав на ссылку "История". 5. Способ отражения указанный для организации, будет формироваться по умолчанию в тех случаях, когда виду начисления или для конкретного сотрудника не назначен свой способ отражения.
```markdown [Вопрос] Что такое способ отражения расходов на оплату труда в "1С:Бухгалтерия 8" (ред. 3.0)? [/Вопрос] [Ответ] Способ отражения расходов на оплату труда в "1С:Бухгалтерия 8" (ред. 3.0) — это настройка, определяющая, как начисления по заработной плате отражаются в бухгалтерском учете организации. Он настраивается в разделе "Зарплата и кадры" через пункт меню "Настройки зарплаты", где выбирается ссылка "Порядок учета зарплаты". На закладке "Зарплата" в разделе "Отражение в учете зарплаты" можно указать способ в поле "Способ отражения в бух.учете". Способ выбирается из справочника "Способы учета зарплаты", который расположен в том же разделе (Зарплата и кадры - Настройки зарплаты - раздел "Отражение в учете" - ссылка "Способы учета зарплаты"). По умолчанию в программе задан "Отражение начислений по умолчанию", который проводит начисления по дебету счета 26 "Общехозяйственные расходы" и кредиту счета 70 "Расчеты с персоналом по оплате труда". Если подходящий способ отсутствует в справочнике, его необходимо создать, указав счет дебета и аналитику проводки. Корреспондирующий счет проводки всегда будет счет 70. Также в поле "Действует с" можно указать период, начиная с которого будет применяться данный способ. Историю изменений можно просмотреть через ссылку "История". Этот способ отражения используется по умолчанию, если никаких других способов не назначено для конкретных начислений или сотрудников. [/Ответ] ------------------------------ [Вопрос] Что такое справочник "Способы учета зарплаты" в "1С:Бухгалтерия 8" (ред. 3.0)? [/Вопрос] [Ответ] Справочник "Способы учета зарплаты" в "1С:Бухгалтерия 8" (ред. 3.0) — это специальный перечень, который содержит допустимые способы бухгалтерского отражения начислений по заработной плате внутри программы. Он находится в разделе "Зарплата и кадры" под "Настройки зарплаты" в подразделе "Отражение в учете" через ссылку "Способы учета зарплаты". Основное назначение этого справочника — предоставлять пользователю возможность выбирать или создавать новые способы учета расходов на оплату труда, которые будут соответствовать учетной политике компании. Каждый способ в справочнике должен включать только счет дебета, который покажет, куда относить расходы по заработной плате, а счетом корреспонденции всегда будет счет 70, отражающий расчеты с персоналом. Эти настройки помогают автоматизировать процесс формирования бухгалтерских проводок по начислениям и гарантируют корректное отражение расходов в учете. [/Ответ] ------------------------------ ```
f66fa85941288aee49dfd9c5En un mod de Minecraft inspirado en super Mario para que el jugador tuviera su skin en las diferentes versiones de los power-ups de super Mario el mod usa filtros que modifica la skin del jugador para poder darle el aspecto.
Eso suena como un mod bastante interesante y creativo. Para implementar un mod de Minecraft donde el jugador pueda tener diferentes aspectos que representen los power-ups de Super Mario, podrías considerar los siguientes aspectos técnicos para la implementación: 1. **Sistema de Power-Ups**: Establece un sistema que permita al jugador obtener y utilizar power-ups, similar a cómo funcionan en los juegos de Super Mario. Esto podría incluir objetos como el Super Champiñón, la Flor de Fuego, la Estrella de Invencibilidad, entre otros. 2. **Modificación de Skin**: Implementa un mecanismo que detecte cuándo un jugador toma un power-up y aplique un filtro o modificación a la skin del jugador. Esto podría lograrse mediante código en el mod que altere temporalmente la textura del personaje para reflejar el power-up activo. 3. **Shaders y Efectos Visuales**: Utiliza shaders o efectos visuales para añadir brillo o cambiar los colores de la skin del jugador, dependiendo del power-up. Por ejemplo, podrías añadir un brillo rojizo para la Flor de Fuego o un destello brillante para la Estrella de Invencibilidad. 4. **Interacciones y Comportamientos**: Aparte del cambio de apariencia, los power-ups deben otorgar habilidades especiales, como disparar bolas de fuego, mayor velocidad, o invulnerabilidad temporal, imitando las mecánicas de Super Mario. 5. **Integración Sutil**: Asegúrate de que los cambios en la skin se integren bien con el estilo visual de Minecraft, manteniendo una estética coherente para no distraer demasiado del aspecto original del juego. 6. **Configuración de Personalización**: Permite a los jugadores personalizar o desactivar este sistema de cambios de skin si prefieren no utilizarlo, ofreciendo opciones de configuración dentro del mod. Implementando estos elementos, puedes crear una experiencia de juego enriquecedora que combine la estética de Super Mario con el mundo abierto y la jugabilidad de Minecraft. Además, asegúrate de probar el mod extensivamente para garantizar que todas las modificaciones de skins y efectos no causen problemas de rendimiento o errores en el juego.
4595c7f6483940b0a0eab314El mod permite elegir el color favorito del jugador (Para que el filtro pueda hacer que los power-ups puedan hacer como en los juegos de Mario con los múltiples personajes).
Agregar la opción de seleccionar un color favorito para personalizar los power-ups es una excelente idea, ya que permite a los jugadores experimentar el mod de una manera más personal y única, similar a cómo en los juegos de Mario cada personaje tiene un esquema de color distintivo. Para implementar esta característica en tu mod, podrías considerar los siguientes pasos: 1. **Interfaz de Usuario para la Selección de Color**: Implementa un menú o pantalla de configuración donde los jugadores puedan elegir su color favorito. Esto podría hacerse a través de un menú de opciones accesible desde el menú principal del mod o una interface en el juego. 2. **Almacenamiento de Preferencias**: Asegúrate de que la selección de color se guarde de forma persistente. Podrías almacenar esta preferencia en un archivo de configuración dentro de la carpeta del mod, o en el sistema de datos del juego, para que no tengan que configurarla cada vez que inicien el juego. 3. **Adaptación de Skins**: Modifica las texturas del mod de forma dinámica usando el color elegido por el jugador. Esto podría hacerse mediante filtros de color que se apliquen a las texturas base de los power-ups, asegurando que los cambios se reflejen instantáneamente. 4. **Compatibilidad con Power-Ups**: Cada power-up debería adaptarse al esquema de color del jugador. Por ejemplo, un Super Champiñón podría cambiar el tamaño del jugador y ajustar su conjunto de colores, mientras que una Flor de Fuego podría mantener los colores de fuego tradicionales pero incluir un toque del color elegido. 5. **Pruebas Visuales**: Como los cambios de color pueden tener efectos visuales significativos, prueba el mod con diferentes combinaciones de colores para garantizar que todas las variaciones se vean bien y no interfieran con la legibilidad o estética del juego. 6. **Feedback del Jugador**: Si es posible, permite a los jugadores proporcionar comentarios sobre el sistema de personalización de colores, para que puedas ajustar el mod según las preferencias y expectativas de la comunidad. Al ofrecer esta opción de personalización, no solo enriqueces la experiencia del jugador al permitirle verse reflejado en el juego, sino que también refuerzas la naturaleza creativa y personalizada de Minecraft, haciendo que el mod sea aún más atractiva y divertida de usar.
c1daa9bbfbf0d36ea3a5309fLos filtros solo funcionan cuando se tiene el mod de Super Mario (El mod que proporciona los power-ups) y el mod Better Player Skin (El mod que diferencia las partes de la skin), por eso la piel no tiene cambios y la ropa si.
En ese caso, al trabajar con dos mods que se complementan - el mod de Super Mario que proporciona los power-ups y el mod Better Player Skin que maneja diferentes partes de la skin - tienes una estructura interesante que permite un alto grado de personalización sin alterar la totalidad de la skin del jugador. Aquí te dejo algunas ideas para manejar esta integración: 1. **Interacción entre Mods**: Asegúrate de que tus mods se comuniquen correctamente. El mod de Super Mario debe enviar señales o eventos cuando un power-up es recogido o utilizado, para que el mod Better Player Skin pueda aplicar los filtros de color adecuados solamente a la ropa y no a la piel del personaje. 2. **Filtros por Partes de la Skin**: Usa el mod Better Player Skin para aplicar colores solo a ciertas partes de la skin. Esto te da la flexibilidad de cambiar los colores de la ropa mientras mantienes la piel del jugador intacta. Podrías implementar una lógica que identifique qué partes representan ropa y aplicar solo allí los cambios de color. 3. **Configuración de Color Preferido**: Como mencionaste previamente, al permitir elegir un color preferido, asegúrate de que este ajuste solo influencie las porciones de la skin que corresponden a la "ropa". Esto puede hacerse mediante un menú de configuración donde el jugador elige su color en el mod de Super Mario, que luego utiliza Better Player Skin para realizar cambios. 4. **Consistencia Visual**: Mantente atento a cómo los cambios de color afectan la apariencia general del jugador en diferentes entornos del juego. Los colores deben ser visibles y agradables en todas las situaciones posibles dentro del juego. 5. **Compatibilidad de Actualización**: Asegúrate de que cualquier actualización que realices en uno de los mods sea compatible con el otro, especialmente si modificas cómo se manejan los eventos o los filtros de color. 6. **Documentación y Soporte**: Proporciona documentación clara para los usuarios sobre cómo instalar y usar ambos mods en conjunto, asegurando una experiencia de usuario fluida. Este sistema de integración modular entre los dos mods permite que los jugadores disfruten de una experiencia personalizada de forma dinámica y eficiente, respetando la estructura de la skin original del jugador mientras experimentan con los divertidos cambios de apariencia inspirados en los power-ups de Super Mario.
b9b8710c1825f0beb5f63b07Implement the sorting from Code 2 in Code 1. Make sure that it works together with the filtering. In the GUI the sorting should be next (right) to the filtering. Also make sure that automatically updates new headers and names in the Dropdown menu of the sorting. Code 1: import ctypes
import csv
import json
import re
import tkinter as tk
from pathlib import Path
from tkinter import filedialog, messagebox, simpledialog, ttk
from typing import Any, Optional
from tksheet import Sheet
def set_dpi_awareness():
"""Enable DPI awareness for high DPI settings."""
try:
ctypes.windll.shcore.SetProcessDpiAwareness(1)
except Exception as e:
print(f"Error setting DPI awareness: {e}")
def custom_filter_design(master, style: ttk.Style, headers=None, filter_types=None, filter_font=None, filter_combobox_font=None, colors=None):
"""Creates a custom filter frame with all components."""
filter_frame = ttk.Frame(master, style="Custom.TFrame")
# Configure styles
style.configure("Custom.TFrame", background=colors["background"], relief="flat", borderwidth=0)
style.configure("TCombobox", relief="flat", borderwidth=0, background=colors["background"], fieldbackground=colors["background"])
style.map("TCombobox", fieldbackground=[('readonly', colors["background"])], background=[('readonly', colors["background"])])
style.configure("TEntry", relief="flat", borderwidth=0, background=colors["background"])
style.configure("Line.TFrame", background=colors["filter_line"])
# Create widgets
header_combobox = ttk.Combobox(filter_frame, values=headers, width=15, font=filter_combobox_font, state="readonly")
filtertype_combobox = ttk.Combobox(filter_frame, values=filter_types, width=15, font=filter_combobox_font, state="readonly")
filter_entry = ttk.Entry(filter_frame, font=filter_font)
underline = ttk.Frame(filter_frame, height=1, style="Line.TFrame")
# Layout widgets
header_combobox.grid(row=0, column=0, padx=(0, 30), pady=2, sticky='ew')
filtertype_combobox.grid(row=0, column=1, padx=2, pady=2, sticky='ew')
filter_entry.grid(row=1, column=0, columnspan=2, padx=2, pady=(2, 0), sticky='ew')
underline.grid(row=2, column=0, columnspan=2, sticky='ew', pady=(0, 2))
# Prevent text selection in comboboxes
for combobox in (header_combobox, filtertype_combobox):
combobox.bind("<FocusIn>", lambda event: event.widget.selection_clear())
# Configure column weights
filter_frame.columnconfigure((0, 1), weight=1)
# Ensure the underline doesn't resize vertically
underline.pack_propagate(False)
return filter_frame, header_combobox, filtertype_combobox, filter_entry
def custom_scrollbar_design(style: ttk.Style, colors=None):
"""Configure custom scrollbar style."""
style.configure("Custom.Vertical.TScrollbar", troughcolor=colors["scrollbar_trough"], background=colors["scrollbar_background"], borderwidth=0, arrowcolor=colors["scrollbar_arrow"])
style.map("Custom.Vertical.TScrollbar", background=[('active', colors["scrollbar_active"])])
style.layout("Custom.Vertical.TScrollbar", [('Vertical.Scrollbar.trough', {'children': [('Vertical.Scrollbar.thumb', {'expand': '1', 'sticky': 'ns'})], 'sticky': 'ns'})])
class TableApp:
"""Table Editor with sidebar and table management."""
def __init__(self, root: tk.Tk):
"""Initialize the TableApp."""
self.root = root
self.config_dimensions()
self.config_fonts()
self.config_colors()
self.initialize_data()
self.style = ttk.Style()
self.style.theme_use("default")
self.setup_ui()
self.load_tables()
def config_dimensions(self):
"""Configure app dimensions."""
self.root.title("Database")
self.root.geometry("1920x1080")
self.root.minsize(1435, 910)
self.dimensions = {
"sidebar_width": 400,
"spacer_frame_width": 100,
"header_height": 190,
"separator_height": 1,
}
def config_fonts(self):
"""Configure app fonts."""
self.fonts = {
"text": ("Literata", 12),
"tksheet": ("Literata", 12, "normal"),
"header_info": ("Literata", 10),
"filter": ("Literata", 12),
"filter_combobox": ("Literata", 8),
}
def config_colors(self):
"""Configure app colors."""
self.colors = {
"text": "#333333",
"background": "#FFFFFF",
"sidebar": "#F7F8FC",
"button": "#F7F8FC",
"button_selected": "#EAEDEF",
"separator": "#333333",
"header_info": "#999999",
"scrollbar_trough": "#F7F8FC",
"scrollbar_background": "#c0c1c4",
"scrollbar_arrow": "#333333",
"scrollbar_active": "#878789",
"filter_line": "#00bcf0",
}
def setup_ui(self):
"""Set up the user interface with a 3x2 grid layout."""
self.root.grid_rowconfigure(0, weight=0)
self.root.grid_rowconfigure(1, weight=1)
self.root.grid_columnconfigure(0, weight=0)
self.root.grid_columnconfigure(1, weight=0, minsize=self.dimensions["spacer_frame_width"])
self.root.grid_columnconfigure(2, weight=3)
self.create_sidebar()
self.create_header_frame()
self.create_spacer_frame()
self.create_table_frame()
def create_sidebar(self):
"""Create the sidebar that spans both rows."""
sidebar = self._create_frame(
parent=self.root,
width=self.dimensions["sidebar_width"],
bg=self.colors["sidebar"]
)
sidebar.grid(row=0, column=0, rowspan=2, sticky='ns')
sidebar.grid_propagate(False)
sidebar.columnconfigure(0, weight=1)
sidebar.rowconfigure(0, weight=1)
functional_sidebar = self._create_frame(
parent=sidebar,
width=self.dimensions["sidebar_width"],
bg=self.colors["sidebar"]
)
functional_sidebar.grid(row=0, column=0, sticky='nsew')
functional_sidebar.columnconfigure(0, weight=1)
functional_sidebar.rowconfigure(0, weight=0)
functional_sidebar.rowconfigure(1, weight=1)
functional_sidebar.rowconfigure(2, weight=0)
self._add_normal_buttons(functional_sidebar)
self._add_scrollable_canvas(functional_sidebar)
self._add_delete_table_section(functional_sidebar)
def create_header_frame(self) -> None:
"""Create the header divided into upper and lower sub-frames."""
header_frame = self._create_and_configure_header_frame()
upper_header_frame = self._create_upper_header(header_frame)
self._create_upper_header_content(upper_header_frame)
lower_header_frame = self._create_lower_header(header_frame)
self._create_filter_controls(lower_header_frame)
def _create_and_configure_header_frame(self) -> tk.Frame:
"""Create and configure the main header frame."""
header_frame = self._create_frame(parent=self.root, width=None, bg=self.colors["background"])
header_frame.grid(row=0, column=1, columnspan=2, sticky='ew')
header_frame.config(height=self.dimensions["header_height"])
header_frame.grid_propagate(False)
for i in range(2):
header_frame.grid_rowconfigure(i, weight=0)
for i in range(5):
weight = 1 if i == 4 else 0
header_frame.grid_columnconfigure(i, weight=weight)
return header_frame
def _create_upper_header(self, parent: tk.Frame) -> tk.Frame:
"""Create the upper header frame."""
upper_header = self._create_frame(parent=parent, width=None, bg=self.colors["background"])
upper_header.grid(row=0, column=0, columnspan=5, sticky='ew', pady=(0, 10))
upper_header.grid_columnconfigure(5, weight=1)
return upper_header
def _create_upper_header_content(self, upper_header: tk.Frame) -> None:
"""Create and add content to the upper header frame."""
self._create_data_label(upper_header)
self._create_table_name_label(upper_header)
self._create_settings_label(upper_header)
def _create_data_label(self, parent: tk.Frame) -> None:
"""Create and grid the 'Data >' label."""
data_label = tk.Label(
parent,
text="Data > ",
bg=self.colors["background"],
fg=self.colors["header_info"],
font=self.fonts["header_info"]
)
data_label.grid(row=0, column=0, padx=22, pady=(22, 0), sticky='nw')
def _create_table_name_label(self, parent: tk.Frame) -> None:
"""Create and grid the table name label."""
self.table_name_label = tk.Label(
parent,
text=self.current_table_name if self.current_table_name else "No Table Selected",
bg=self.colors["background"],
fg=self.colors["text"],
font=self.fonts["header_info"]
)
self.table_name_label.grid(row=0, column=0, padx=(88, 10), pady=(22, 0), sticky='nw')
def _create_settings_label(self, parent: tk.Frame) -> None:
"""Create and grid the settings label with its associated menu."""
settings_label = tk.Label(
parent,
text="...",
bg=self.colors["background"],
fg=self.colors["text"],
cursor="hand2",
anchor='e',
font=self.fonts["text"]
)
settings_label.grid(row=0, column=5, padx=40, pady=20, sticky='e')
settings_menu = self._create_settings_menu()
self._bind_settings_label_events(settings_label, settings_menu)
def _create_settings_menu(self) -> tk.Menu:
"""Create the settings dropdown menu."""
menu = tk.Menu(self.root, tearoff=0)
menu.add_command(label="Export Table", command=self.export_table)
menu.add_command(label="Import Table", command=self.import_file)
menu.add_command(label="Rename Headers", command=self.rename_headers)
return menu
def _bind_settings_label_events(self, label: tk.Label, menu: tk.Menu) -> None:
"""Bind events to the settings label."""
label.bind("<Button-1>", lambda e: menu.post(e.x_root, e.y_root))
label.bind("<Enter>", lambda _: label.config(bg=self.colors["button_selected"]))
label.bind("<Leave>", lambda _: label.config(bg=self.colors["background"]))
def _create_lower_header(self, parent: tk.Frame) -> tk.Frame:
"""Create the lower header frame."""
lower_header = self._create_frame(parent=parent, width=None, bg=self.colors["background"])
lower_header.grid(row=1, column=0, columnspan=5, sticky='ew')
return lower_header
def _create_filter_controls(self, parent: tk.Frame) -> None:
"""Create and add filter controls to the lower header frame."""
filter_frame, self.filter_header_dropdown, self.filter_option_dropdown, self.filter_text_entry = custom_filter_design(parent, self.style, headers=["All"] + self.headers.get(self.current_table_name, []), filter_types=[
"is", "is not", "contains", "does not contain",
"starts with", "ends with", "is empty", "is not empty"
], filter_font=self.fonts["filter"], filter_combobox_font=self.fonts["filter_combobox"], colors=self.colors)
filter_frame.grid(row=0, column=1, columnspan=4, padx=100, pady=0, sticky='ew')
self.filter_header_var.set("All")
self.filter_option_var.set("contains")
self.filter_text_var.set("")
self.filter_header_dropdown['textvariable'] = self.filter_header_var
self.filter_option_dropdown['textvariable'] = self.filter_option_var
self.filter_text_entry['textvariable'] = self.filter_text_var
self.filter_header_dropdown.bind("<<ComboboxSelected>>", self.apply_filter)
self.filter_option_dropdown.bind("<<ComboboxSelected>>", lambda event: [self.update_filter_text_state(event), self.apply_filter()])
self.filter_text_entry.bind("<KeyRelease>", self.apply_filter)
self.filter_text_entry.bind("<Return>", self.apply_filter)
def update_filter_text_state(self, event=None):
"""Enable or disable the filter text entry based on filter option."""
option = self.filter_option_var.get()
if option in ["is empty", "is not empty"]:
self.filter_text_var.set("")
self.filter_text_entry.config(state='disabled')
self.apply_filter()
else:
self.filter_text_entry.config(state='normal')
def create_spacer_frame(self):
"""Create a spacer frame in column 1, row 1."""
spacer = self._create_frame(
parent=self.root,
width=self.dimensions["spacer_frame_width"],
bg=self.colors["background"]
)
spacer.grid(row=1, column=1, sticky='nsew')
spacer.grid_propagate(False)
def create_table_frame(self):
"""Create the table frame in column 2, row 1."""
self.table_frame = self._create_frame(
parent=self.root,
width=None,
bg=self.colors["background"]
)
self.table_frame.grid(row=1, column=2, sticky='nsew')
self.table_frame.grid_rowconfigure(0, weight=1)
self.table_frame.grid_columnconfigure(0, weight=1)
def _create_frame(
self,
parent: tk.Widget,
width: Optional[int],
bg: str
) -> tk.Frame:
"""Create a frame with specified width and background."""
frame = tk.Frame(parent, width=width, bg=bg, bd=0, relief='flat')
if width:
frame.config(width=width)
return frame
def _add_normal_buttons(self, sidebar: tk.Frame):
"""Add buttons to the sidebar."""
normal_buttons_frame = self._create_frame(
parent=sidebar,
width=self.dimensions["sidebar_width"],
bg=self.colors["sidebar"]
)
normal_buttons_frame.grid(row=0, column=0, sticky='ew', padx=10, pady=(10, 0))
normal_buttons_frame.columnconfigure(0, weight=1)
create_table_label = self._create_clickable_label(
parent=normal_buttons_frame,
text="Create New Table",
command=self.create_new_table,
anchor='center'
)
create_table_label.grid(row=0, column=0, pady=10, sticky='ew')
self.add_custom_hover(create_table_label)
separator = tk.Frame(normal_buttons_frame, height=self.dimensions["separator_height"], bg=self.colors["separator"])
separator.grid(row=1, column=0, sticky='ew', pady=5)
def _on_canvas_configure(self, event):
"""Update the table_labels_frame width to match the canvas width."""
self.canvas.itemconfig(self.table_labels_window, width=event.width)
def _add_scrollable_canvas(self, sidebar: tk.Frame):
"""Add scrollable canvas for table labels within a single frame."""
scrollable_container = self._create_frame(
parent=sidebar,
width=self.dimensions["sidebar_width"],
bg=self.colors["sidebar"]
)
scrollable_container.grid(row=1, column=0, sticky='nsew', padx=10, pady=10)
scrollable_container.columnconfigure(0, weight=1)
scrollable_container.rowconfigure(0, weight=1)
self.canvas = tk.Canvas(
scrollable_container,
bg=self.colors["sidebar"],
highlightthickness=0
)
self.canvas.grid(row=0, column=0, sticky='nsew')
custom_scrollbar_design(self.style, self.colors)
self.scrollbar = ttk.Scrollbar(
scrollable_container,
orient="vertical",
command=self.canvas.yview,
style="Custom.Vertical.TScrollbar"
)
self.scrollbar.grid(row=0, column=1, sticky='ns')
self.canvas.configure(yscrollcommand=self.scrollbar.set)
self.table_labels_frame = tk.Frame(self.canvas, bg=self.colors["sidebar"])
# Capture the window item ID
self.table_labels_window = self.canvas.create_window((0, 0), window=self.table_labels_frame, anchor="nw", width=self.dimensions["sidebar_width"])
self.canvas.bind("<MouseWheel>", self._on_mouse_wheel)
self.table_labels_frame.bind("<MouseWheel>", self._on_mouse_wheel)
self.table_labels_frame.bind(
"<Configure>",
lambda event: (
self.canvas.configure(scrollregion=self.canvas.bbox("all")),
self._update_scrollbar(scrollable_container, self.canvas, self.scrollbar)
)
)
scrollable_container.bind(
"<Configure>",
lambda event: self._update_scrollbar(scrollable_container, self.canvas, self.scrollbar)
)
# Bind the canvas resize to update the table_labels_frame width
self.canvas.bind('<Configure>', self._on_canvas_configure)
def _on_mouse_wheel(self, event):
"""Handle mouse wheel scrolling only if scrollbar is visible."""
if self.scrollbar.winfo_viewable():
self.canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
def _update_scrollbar(self, scrollbar_frame, canvas, scrollbar):
"""Update scrollbar visibility based on content size."""
canvas.update_idletasks()
canvas_height = scrollbar_frame.winfo_height()
content_height = self.table_labels_frame.winfo_reqheight()
if content_height > canvas_height:
scrollbar.grid()
canvas.configure(yscrollcommand=scrollbar.set)
canvas.bind("<MouseWheel>", lambda event: canvas.yview_scroll(int(-1 * (event.delta / 120)), "units"))
else:
scrollbar.grid_remove()
canvas.configure(yscrollcommand=lambda *args: None)
canvas.unbind("<MouseWheel>")
def _add_delete_table_section(self, sidebar: tk.Frame):
"""Add delete table option to the sidebar."""
delete_table_frame = self._create_frame(
parent=sidebar,
width=self.dimensions["sidebar_width"],
bg=self.colors["sidebar"]
)
delete_table_frame.grid(row=2, column=0, sticky='ew', padx=10, pady=(0, 10))
delete_table_frame.columnconfigure(0, weight=1)
delete_table_frame.rowconfigure(1, weight=1)
separator = tk.Frame(delete_table_frame, height=self.dimensions["separator_height"], bg=self.colors["separator"])
separator.grid(row=0, column=0, sticky='ew', pady=5)
delete_table_label = self._create_clickable_label(
parent=delete_table_frame,
text="🗑 Delete Selected Table",
command=self.delete_table,
anchor='center'
)
delete_table_label.grid(row=1, column=0, pady=10, sticky='ew')
self.add_custom_hover(delete_table_label)
def _create_clickable_label(
self,
parent: tk.Widget,
text: str,
command,
allow_rename: bool = False,
anchor: str = 'w'
) -> tk.Label:
"""Create a clickable label with optional rename functionality."""
cursor = "arrow" if allow_rename else "hand2"
label = tk.Label(
parent,
text=text,
bg=self.colors["button"],
fg=self.colors["text"],
cursor=cursor,
anchor=anchor,
font=self.fonts["text"]
)
label.bind("<Button-1>", lambda _: command())
if allow_rename:
label.bind("<Double-Button-1>", lambda _: self.begin_rename(text))
label.bind("<MouseWheel>", self._on_mouse_wheel)
return label
def add_custom_hover(self, widget: tk.Widget):
"""Add custom hover effects to a widget."""
widget.bind("<Enter>", lambda e: widget.config(bg=self.colors["button_selected"]))
widget.bind("<Leave>", lambda e: widget.config(bg=self.colors["button"]))
def initialize_data(self):
"""Initialize data structures."""
self.save_file_path = Path(__file__).parent / "Data.json"
self.tables_data: dict[str, dict] = {}
self.current_sheet: Sheet | None = None
self.headers: dict[str, list[str]] = {}
self.current_table_name: str | None = None
self.rename_entry: tk.Entry | None = None
self.selected_table_label: tk.Label | None = None
self.table_labels: dict[str, tk.Label] = {}
self.table_name_label = None
self.filter_header_var = tk.StringVar(value="All")
self.filter_option_var = tk.StringVar(value="contains")
self.filter_text_var = tk.StringVar()
# Added: Initialize displayed_row_indices
self.displayed_row_indices: list[int] = []
def sort_table_labels(self):
"""Sort table labels with numeric names first, then alphabetic, then alphanumeric."""
def sort_key(name):
# Category 1: Starts with digits (e.g., "1. Movies")
m = re.match(r'^(\d+)', name)
if m:
return (1, int(m.group(1)))
# Category 3: Starts with letters followed by digits (e.g., "T1", "T2")
m = re.match(r'^([A-Za-z]+)(\d+)', name)
if m:
return (3, m.group(1).lower(), int(m.group(2)))
# Category 2: Starts with letters only (e.g., "a", "b", "c")
m = re.match(r'^([A-Za-z]+)', name)
if m:
return (2, m.group(1).lower())
# Category 4: Any other names
return (4, name.lower())
# Sort the table names using the defined sort key
sorted_tables = sorted(self.tables_data.keys(), key=sort_key)
# Re-pack the labels in the sorted order
for table_name in sorted_tables:
label = self.table_labels.get(table_name)
if label:
label.pack_forget()
label.pack(fill='x', padx=0, pady=5, expand=True)
def create_new_table(self):
"""Create a new table."""
existing_numbers = [int(name.split(" ")[1]) for name in self.tables_data if name.startswith("Table ") and name.split(" ")[1].isdigit()]
next_number = max(existing_numbers, default=0) + 1
table_name = f"Table {next_number}"
self.add_table(table_name)
self.load_selected_table(table_name)
def import_file(self):
"""Import table from file."""
file_path = filedialog.askopenfilename(title="Import File", filetypes=[("All Supported Files", "*.json *.csv"), ("JSON files", "*.json"), ("CSV files", "*.csv")])
if not file_path:
return
try:
if file_path.endswith('.csv'):
with open(file_path, newline='', encoding='utf-8') as csvfile:
data = list(csv.reader(csvfile))
elif file_path.endswith('.json'):
with open(file_path, 'r', encoding='utf-8') as jsonfile:
data = json.load(jsonfile)
else:
raise ValueError("Unsupported file format.")
if not data or not isinstance(data, list) or not all(isinstance(row, list) for row in data):
raise ValueError("Invalid data format in the file.")
headers, table_data = data[0], data[1:]
self.add_table_from_data(file_path, headers, table_data)
except Exception as e:
messagebox.showerror("File Import Error", str(e))
def add_table_from_data(self, file_path: str, headers: list[str], table_data: list[list[str]]):
"""Add a table from file data."""
table_name = Path(file_path).stem
table_name = self.ensure_unique_table_name(table_name)
self.add_table(table_name, data=table_data, headers=headers)
self.load_selected_table(table_name)
self.save_data()
def ensure_unique_table_name(self, table_name: str) -> str:
"""Ensure table name is unique."""
original_name = table_name
counter = 1
while table_name in self.tables_data:
table_name = f"{original_name} (New {counter})"
counter += 1
return table_name
def save_table_data(self, file_path: str, data: list[list[str]], format: str):
"""Save table data in specified format."""
match format:
case 'csv':
with open(file_path, 'w', newline='', encoding='utf-8') as file:
csv.writer(file).writerows(data)
case 'json':
with open(file_path, 'w', encoding='utf-8') as file:
json.dump(data, file, indent=4)
case _:
raise ValueError("Unsupported file format.")
def export_table(self):
"""Export current table to file."""
if not self.current_table_name or self.current_table_name not in self.tables_data:
messagebox.showwarning("No Table Selected", "Please select a table to export.")
return
file_path = filedialog.asksaveasfilename(title="Export File", defaultextension=".json", filetypes=[("JSON files", "*.json"), ("CSV files", "*.csv")], initialfile=self.current_table_name)
if not file_path:
return
try:
data = [self.headers[self.current_table_name]] + self.tables_data[self.current_table_name]["data"]
if file_path.endswith('.csv'):
self.save_table_data(file_path, data, 'csv')
elif file_path.endswith('.json'):
with open(file_path, 'w', encoding='utf-8') as file:
json.dump(data, file, indent=4)
else:
raise ValueError("Unsupported file format.")
self.save_data()
messagebox.showinfo("Export Successful", f"Table '{self.current_table_name}' has been exported successfully.")
except Exception as e:
messagebox.showerror("Export Failed", f"An error occurred during export: {e}")
def add_table(self, table_name: str, data: list[list[str]] | None = None, headers: Optional[list[str]] = None):
"""Add a new table."""
if headers:
self.headers[table_name] = headers
else:
default_headers = ["Column 1", "Column 2", "Column 3", "Column 4", "Column 5"]
self.headers[table_name] = default_headers
self.tables_data[table_name] = {"headers": self.headers[table_name], "data": data or [["" for _ in range(len(self.headers[table_name]))] for _ in range(10)]}
self.create_table_label(table_name)
self.save_data()
def create_table_label(self, table_name: str):
"""Create label for table."""
label = self._create_clickable_label(self.table_labels_frame, table_name, lambda: self.load_selected_table(table_name), True)
label.pack(fill='x', padx=0, pady=5, expand=True)
self.table_labels[table_name] = label
self.add_custom_hover(label)
self.sort_table_labels()
def delete_table(self):
"""Delete selected table."""
if not self.current_table_name or self.current_table_name not in self.tables_data:
messagebox.showwarning("No Table Selected", "Please select a valid table to delete.")
return
if messagebox.askyesno("Delete Table", f"Are you sure you want to delete the table '{self.current_table_name}'?"):
self.remove_table(self.current_table_name)
def on_columns_deleted(self, event):
"""Handle column deletion and update filter headers."""
if not self.current_table_name or not self.current_sheet:
return
new_headers = self.current_sheet.headers()
self.headers[self.current_table_name] = new_headers
self.filter_header_dropdown['values'] = ["All"] + new_headers
self.filter_header_var.set("All")
self.filter_option_var.set("contains")
self.filter_text_var.set("")
self.filter_text_entry.config(state='normal')
self.apply_filter()
def remove_table(self, table_name: str):
"""Remove a table."""
if self.current_table_name == table_name and self.current_sheet:
self.current_sheet.destroy()
self.current_sheet = None
label = self.table_labels.get(table_name)
if label:
label.destroy()
if table_name in self.tables_data:
del self.tables_data[table_name]
if table_name in self.headers:
del self.headers[table_name]
if table_name in self.table_labels:
del self.table_labels[table_name]
self.current_table_name = None
self.selected_table_label = None
self.save_data()
self.sort_table_labels()
def load_tables(self):
"""Load tables from file."""
if not self.save_file_path.exists():
return
try:
with self.save_file_path.open('r', encoding='utf-8') as file:
all_data = json.load(file)
except Exception as e:
messagebox.showerror("Load Error", f"Failed to load tables: {e}")
return
for table_name, data in all_data.items():
if not isinstance(data, list) or not data:
continue
headers, table_data = data[0], data[1:]
self.headers[table_name] = headers
self.tables_data[table_name] = {"headers": headers, "data": table_data}
self.create_table_label(table_name)
self.sort_table_labels()
def load_selected_table(self, table_name: str):
"""Display selected table."""
if self.current_sheet:
self.current_sheet.pack_forget()
self.current_sheet.destroy()
self.current_sheet = None
if self.selected_table_label:
try:
self.selected_table_label.config(bg=self.colors["button"])
except tk.TclError:
self.selected_table_label = None
if table_name not in self.tables_data:
messagebox.showerror("Error", f"Table '{table_name}' does not exist.")
self.current_table_name = None
self.selected_table_label = None
return
table_info = self.tables_data[table_name]
headers = table_info["headers"]
data = table_info["data"]
try:
sheet = Sheet(
self.table_frame,
data=data,
show_table=True,
headers=headers,
font=self.fonts["tksheet"],
header_font=self.fonts["tksheet"],
auto_resize_columns=True
)
sheet.set_options(table_fg=self.colors["text"], header_fg=self.colors["text"])
sheet.enable_bindings()
sheet.extra_bindings([
("end_edit_cell", self.on_cell_edit),
("end_delete_columns", self.on_columns_deleted) # Added binding for column deletion
])
sheet.pack(expand=True, fill='both')
self.current_sheet = sheet
self.current_table_name = table_name
if self.table_name_label:
self.table_name_label.config(text=self.current_table_name)
else:
self.table_name_label = tk.Label(
self.root,
text=self.current_table_name,
bg=self.colors["background"],
fg=self.colors["text"],
font=self.fonts["header_info"]
)
self.table_name_label.grid(row=0, column=0, padx=(60, 10), pady=(5, 0), sticky='nw')
label = self.table_labels.get(table_name)
if label:
self.selected_table_label = label
try:
self.selected_table_label.config(bg=self.colors["button_selected"])
except tk.TclError:
self.selected_table_label = None
messagebox.showerror("Error", f"Failed to update the label for '{table_name}'.")
else:
self.selected_table_label = None
messagebox.showerror("Error", f"Label for '{table_name}' not found.")
self.filter_header_var.set("All")
self.filter_header_dropdown['values'] = ["All"] + headers
self.filter_option_var.set("contains")
self.update_filter_text_state()
self.apply_filter()
except Exception as e:
messagebox.showerror("Sheet Error", f"Failed to load table '{table_name}': {e}")
self.current_table_name = None
self.selected_table_label = None
def on_cell_edit(self, event=None):
"""Handle cell edits."""
if self.current_table_name and self.current_sheet:
sheet_data = self.current_sheet.get_sheet_data()
for idx, row in enumerate(sheet_data):
if idx < len(self.displayed_row_indices):
data_idx = self.displayed_row_indices[idx]
self.tables_data[self.current_table_name]["data"][data_idx] = row
else:
# Added: Handle new rows by appending to the underlying data
self.tables_data[self.current_table_name]["data"].append(row)
self.save_data()
def save_data(self):
"""Save all tables data."""
all_data = {}
for table_name, table_info in self.tables_data.items():
all_data[table_name] = [table_info["headers"]] + table_info["data"]
try:
with self.save_file_path.open('w', encoding='utf-8') as file:
json.dump(all_data, file, indent=4)
except Exception as e:
print(f"Error saving data: {e}")
def apply_filter(self, event=None):
"""Apply filter to table data based on selected criteria."""
if not self.current_table_name or not self.current_sheet:
return
header = self.filter_header_var.get()
option = self.filter_option_var.get()
text = self.filter_text_var.get().lower()
original_data = self.tables_data[self.current_table_name]["data"]
def row_matches(row):
if header == "All":
cells = row
target_indices = range(len(cells))
else:
try:
target_indices = [self.headers[self.current_table_name].index(header)]
except ValueError:
return False
conditions = {
"is": lambda cell, text: cell == text,
"is not": lambda cell, text: cell != text,
"contains": lambda cell, text: text in cell,
"does not contain": lambda cell, text: text not in cell,
"starts with": lambda cell, text: cell.startswith(text),
"ends with": lambda cell, text: cell.endswith(text),
"is empty": lambda cell, _: cell == "",
"is not empty": lambda cell, _: cell != ""
}
for idx in target_indices:
cell = str(row[idx]).lower()
if conditions.get(option, lambda _, __: False)(cell, text):
return True
return False
# Collect both filtered data and their original indices
filtered_data = []
self.displayed_row_indices = []
for i, row in enumerate(original_data):
if row_matches(row):
filtered_data.append(row)
self.displayed_row_indices.append(i)
try:
self.current_sheet.set_sheet_data(filtered_data)
except tk.TclError as e:
print(f"Error applying filter: {e}")
def rename_headers(self):
"""Rename table headers."""
if not self.current_table_name:
messagebox.showwarning("No Table Selected", "Please select a table to rename headers.")
return
current_headers = self.headers[self.current_table_name]
new_headers = simpledialog.askstring(
"Rename Headers",
"Enter new headers (comma-separated):",
initialvalue=", ".join(current_headers)
)
if new_headers:
header_list = [header.strip() for header in new_headers.split(',')]
if not header_list:
messagebox.showerror("Invalid Headers", "Header list cannot be empty.")
return
self.headers[self.current_table_name] = header_list
self.tables_data[self.current_table_name]["headers"] = header_list
if self.current_sheet:
try:
self.current_sheet.headers(header_list)
except tk.TclError as e:
messagebox.showerror("Error", f"Failed to rename headers: {e}")
self.filter_header_var.set("All")
self.filter_header_dropdown['values'] = ["All"] + header_list
self.apply_filter()
self.save_data()
def begin_rename(self, table_name: str):
"""Start renaming a table."""
if table_name not in self.table_labels:
messagebox.showerror("Error", f"Table '{table_name}' not found.")
return
label = self.table_labels[table_name]
self.cancel_rename()
self.rename_entry = tk.Entry(self.table_labels_frame, bg=label.cget("bg"), fg=self.colors["text"], relief='flat')
self.rename_entry.insert(0, table_name)
self.rename_entry.place(x=label.winfo_x(), y=label.winfo_y(), width=label.winfo_width(), height=label.winfo_height())
self.rename_entry.focus()
self.rename_entry.select_range(0, tk.END)
self.rename_entry.bind('<Return>', lambda event: self.end_rename(table_name))
self.rename_entry.bind('<FocusOut>', lambda event: self.cancel_rename())
def end_rename(self, old_name: str):
"""Finalize table renaming."""
new_name = self.rename_entry.get().strip()
if not new_name:
messagebox.showerror("Invalid Name", "Table name cannot be empty.")
self.rename_entry.focus_set()
return
if new_name in self.tables_data and new_name != old_name:
messagebox.showerror("Duplicate Name", f"A table named '{new_name}' already exists.")
self.rename_entry.focus_set()
return
if new_name != old_name:
self.update_table_name(old_name, new_name)
self.load_selected_table(new_name)
self.cancel_rename()
def update_table_name(self, old_name: str, new_name: str):
"""Update table name and resources."""
self.tables_data[new_name] = self.tables_data.pop(old_name)
self.headers[new_name] = self.headers.pop(old_name)
label = self.table_labels.pop(old_name)
label.config(text=new_name)
label.bind("<Button-1>", lambda event: self.load_selected_table(new_name))
label.bind("<Double-1>", lambda event: self.begin_rename(new_name))
self.table_labels[new_name] = label
if self.selected_table_label == label:
try:
label.config(bg=self.colors["button_selected"])
except tk.TclError:
pass
self.save_data()
self.sort_table_labels()
def cancel_rename(self):
"""Cancel rename operation."""
if self.rename_entry:
self.rename_entry.destroy()
self.rename_entry = None
def add_custom_hover(self, label: tk.Label):
"""Add hover effects to label."""
def on_enter(event):
if label != self.selected_table_label:
try:
label.config(bg=self.colors["button_selected"])
except tk.TclError:
pass
def on_leave(event):
if label != self.selected_table_label:
try:
label.config(bg=self.colors["button"])
except tk.TclError:
pass
label.bind("<Enter>", on_enter)
label.bind("<Leave>", on_leave)
if __name__ == "__main__":
set_dpi_awareness()
root = tk.Tk()
app = TableApp(root)
root.mainloop()
Code 2: from tksheet import Sheet
import tkinter as tk
from tkinter import ttk
def custom_sort_design(master, style: ttk.Style, headers=None, sort_orders=None, colors=None):
"""Creates a custom sort frame with comboboxes for columns and sort order, includes secondary sort."""
sort_frame = ttk.Frame(master, style="Custom.TFrame")
# Configure styles
style.configure("Custom.TFrame", background=colors["background"], relief="flat", borderwidth=0)
style.configure("TCombobox", relief="flat", borderwidth=0, background=colors["background"], fieldbackground=colors["background"])
style.map("TCombobox", fieldbackground=[('readonly', colors["background"])], background=[('readonly', colors["background"])])
style.configure("Line.TFrame", background=colors["filter_line"])
# Create widgets for primary sort
primary_column_combobox = ttk.Combobox(sort_frame, values=headers, width=10, font=('Arial', 10), state="readonly")
primary_order_combobox = ttk.Combobox(sort_frame, values=sort_orders, width=10, font=('Arial', 10), state="readonly")
# Create widgets for secondary sort
secondary_column_combobox = ttk.Combobox(sort_frame, values=headers, width=10, font=('Arial', 10), state="readonly")
secondary_order_combobox = ttk.Combobox(sort_frame, values=sort_orders, width=10, font=('Arial', 10), state="readonly")
# Layout widgets
primary_column_combobox.grid(row=0, column=0, padx=5, pady=2, sticky='ew')
primary_order_combobox.grid(row=0, column=1, padx=5, pady=2, sticky='ew')
secondary_column_combobox.grid(row=1, column=0, padx=5, pady=2, sticky='ew')
secondary_order_combobox.grid(row=1, column=1, padx=5, pady=2, sticky='ew')
# Prevent text selection in comboboxes
for combobox in (primary_column_combobox, primary_order_combobox, secondary_column_combobox, secondary_order_combobox):
combobox.bind("<FocusIn>", lambda event: event.widget.selection_clear())
# Configure column weights
sort_frame.columnconfigure((0, 1), weight=1)
return (
sort_frame,
primary_column_combobox,
primary_order_combobox,
secondary_column_combobox,
secondary_order_combobox
)
class SortableSheetApp(tk.Tk):
"""Tkinter application with a sortable sheet."""
def __init__(self):
"""Initialize the sortable sheet application."""
super().__init__()
self.title("Sortable TkSheet Example")
self.data = [
["Apple", 10, 2.5],
["Banana", 5, 1.0],
["Apple", 15, 3.0],
["Orange", 8, 2.0],
["Banana", 12, 2.8],
["Apple", 20, 4.0],
["Orange", 10, 2.5],
]
self.headers = ["Fruit", "Quantity", "Price"]
self.sort_orders = ["Ascending", "Descending"]
# Define style
self.style = ttk.Style(self)
colors = {
"background": "#f0f0f0",
"filter_line": "#d9d9d9",
}
# Create custom sort frame
(
self.sort_frame,
self.primary_column_combobox,
self.primary_order_combobox,
self.secondary_column_combobox,
self.secondary_order_combobox
) = custom_sort_design(self, self.style, headers=self.headers, sort_orders=self.sort_orders, colors=colors)
self.sort_frame.pack(side="top", fill="x")
self.sheet = Sheet(self, data=self.data, headers=self.headers)
self.sheet.enable_bindings("all")
self.sheet.pack(expand=True, fill="both")
# Bind combobox changes to sorting
self.primary_column_combobox.bind("<<ComboboxSelected>>", lambda _: self.sort_data())
self.primary_order_combobox.bind("<<ComboboxSelected>>", lambda _: self.sort_data())
self.secondary_column_combobox.bind("<<ComboboxSelected>>", lambda _: self.sort_data())
self.secondary_order_combobox.bind("<<ComboboxSelected>>", lambda _: self.sort_data())
# Set default selections and sort initially
self.primary_column_combobox.set(self.headers[0])
self.primary_order_combobox.set(self.sort_orders[0])
self.secondary_column_combobox.set(self.headers[0])
self.secondary_order_combobox.set(self.sort_orders[0])
self.sort_data()
def sort_data(self):
"""Sort data based on the dropdown selections for primary and secondary sorts."""
primary_column = self.primary_column_combobox.get()
primary_ascending = self.primary_order_combobox.get() == "Ascending"
secondary_column = self.secondary_column_combobox.get()
secondary_ascending = self.secondary_order_combobox.get() == "Ascending"
primary_index = self.headers.index(primary_column)
secondary_index = self.headers.index(secondary_column)
# Sort the data
self.data.sort(
key=lambda row: (
self.get_sortable_value(row[primary_index]),
self.get_sortable_value(row[secondary_index])
),
reverse=not primary_ascending
)
self.sheet.set_sheet_data(self.data)
def get_sortable_value(self, value):
"""Convert value to a sortable format."""
return str(value) # Convert everything to a string for default sorting behavior
if __name__ == "__main__":
app = SortableSheetApp()
app.mainloop()
To integrate the sorting functionality from **Code 2** into **Code 1**, we'll follow these steps:
1. **Create a Custom Sort Design**: Similar to the filter controls, we'll create a sort frame with comboboxes for selecting primary and secondary sort columns and their respective sort orders.
2. **Integrate Sorting into the Header**: We'll place the sort controls next to the filter controls in the header frame.
3. **Dynamic Updating of Sort Options**: Ensure that the sort combobox options update automatically when headers are renamed or new headers are added.
4. **Apply Sorting in Conjunction with Filtering**: After filtering the data, the sorted order will be applied to the filtered results.
Below is the updated **Code 1** with the necessary modifications to include sorting functionality.
```python
import ctypes
import csv
import json
import re
import tkinter as tk
from pathlib import Path
from tkinter import filedialog, messagebox, simpledialog, ttk
from typing import Any, Optional
from tksheet import Sheet
def set_dpi_awareness():
"""Enable DPI awareness for high DPI settings."""
try:
ctypes.windll.shcore.SetProcessDpiAwareness(1)
except Exception as e:
print(f"Error setting DPI awareness: {e}")
def custom_filter_design(master, style: ttk.Style, headers=None, filter_types=None, filter_font=None, filter_combobox_font=None, colors=None):
"""Creates a custom filter frame with all components."""
filter_frame = ttk.Frame(master, style="Custom.TFrame")
# Configure styles
style.configure("Custom.TFrame", background=colors["background"], relief="flat", borderwidth=0)
style.configure("TCombobox", relief="flat", borderwidth=0, background=colors["background"], fieldbackground=colors["background"])
style.map("TCombobox", fieldbackground=[('readonly', colors["background"])], background=[('readonly', colors["background"])])
style.configure("TEntry", relief="flat", borderwidth=0, background=colors["background"])
style.configure("Line.TFrame", background=colors["filter_line"])
# Create widgets
header_combobox = ttk.Combobox(filter_frame, values=headers, width=15, font=filter_combobox_font, state="readonly")
filtertype_combobox = ttk.Combobox(filter_frame, values=filter_types, width=15, font=filter_combobox_font, state="readonly")
filter_entry = ttk.Entry(filter_frame, font=filter_font)
underline = ttk.Frame(filter_frame, height=1, style="Line.TFrame")
# Layout widgets
header_combobox.grid(row=0, column=0, padx=(0, 30), pady=2, sticky='ew')
filtertype_combobox.grid(row=0, column=1, padx=2, pady=2, sticky='ew')
filter_entry.grid(row=1, column=0, columnspan=2, padx=2, pady=(2, 0), sticky='ew')
underline.grid(row=2, column=0, columnspan=2, sticky='ew', pady=(0, 2))
# Prevent text selection in comboboxes
for combobox in (header_combobox, filtertype_combobox):
combobox.bind("<FocusIn>", lambda event: event.widget.selection_clear())
# Configure column weights
filter_frame.columnconfigure((0, 1), weight=1)
# Ensure the underline doesn't resize vertically
underline.pack_propagate(False)
return filter_frame, header_combobox, filtertype_combobox, filter_entry
def custom_sort_design(master, style: ttk.Style, headers=None, sort_orders=None, sort_font=None, sort_combobox_font=None, colors=None):
"""Creates a custom sort frame with comboboxes for primary and secondary sorts."""
sort_frame = ttk.Frame(master, style="Custom.TFrame")
# Configure styles
style.configure("Custom.TFrame", background=colors["background"], relief="flat", borderwidth=0)
style.configure("TCombobox", relief="flat", borderwidth=0, background=colors["background"], fieldbackground=colors["background"])
style.map("TCombobox", fieldbackground=[('readonly', colors["background"])], background=[('readonly', colors["background"])])
style.configure("Line.TFrame", background=colors["filter_line"])
# Create widgets for primary sort
primary_column_combobox = ttk.Combobox(sort_frame, values=headers, width=15, font=sort_combobox_font, state="readonly")
primary_order_combobox = ttk.Combobox(sort_frame, values=sort_orders, width=15, font=sort_combobox_font, state="readonly")
# Create widgets for secondary sort
secondary_column_combobox = ttk.Combobox(sort_frame, values=headers, width=15, font=sort_combobox_font, state="readonly")
secondary_order_combobox = ttk.Combobox(sort_frame, values=sort_orders, width=15, font=sort_combobox_font, state="readonly")
# Layout widgets
primary_column_combobox.grid(row=0, column=0, padx=5, pady=2, sticky='ew')
primary_order_combobox.grid(row=0, column=1, padx=5, pady=2, sticky='ew')
secondary_column_combobox.grid(row=1, column=0, padx=5, pady=2, sticky='ew')
secondary_order_combobox.grid(row=1, column=1, padx=5, pady=2, sticky='ew')
# Prevent text selection in comboboxes
for combobox in (primary_column_combobox, primary_order_combobox, secondary_column_combobox, secondary_order_combobox):
combobox.bind("<FocusIn>", lambda event: event.widget.selection_clear())
# Configure column weights
sort_frame.columnconfigure((0, 1), weight=1)
return (
sort_frame,
primary_column_combobox,
primary_order_combobox,
secondary_column_combobox,
secondary_order_combobox
)
def custom_scrollbar_design(style: ttk.Style, colors=None):
"""Configure custom scrollbar style."""
style.configure("Custom.Vertical.TScrollbar", troughcolor=colors["scrollbar_trough"], background=colors["scrollbar_background"], borderwidth=0, arrowcolor=colors["scrollbar_arrow"])
style.map("Custom.Vertical.TScrollbar", background=[('active', colors["scrollbar_active"])])
style.layout("Custom.Vertical.TScrollbar", [('Vertical.Scrollbar.trough', {'children': [('Vertical.Scrollbar.thumb', {'expand': '1', 'sticky': 'ns'})], 'sticky': 'ns'})])
class TableApp:
"""Table Editor with sidebar and table management."""
def __init__(self, root: tk.Tk):
"""Initialize the TableApp."""
self.root = root
self.config_dimensions()
self.config_fonts()
self.config_colors()
self.initialize_data()
self.style = ttk.Style()
self.style.theme_use("default")
self.setup_ui()
self.load_tables()
def config_dimensions(self):
"""Configure app dimensions."""
self.root.title("Database")
self.root.geometry("1920x1080")
self.root.minsize(1435, 910)
self.dimensions = {
"sidebar_width": 400,
"spacer_frame_width": 100,
"header_height": 190,
"separator_height": 1,
}
def config_fonts(self):
"""Configure app fonts."""
self.fonts = {
"text": ("Literata", 12),
"tksheet": ("Literata", 12, "normal"),
"header_info": ("Literata", 10),
"filter": ("Literata", 12),
"filter_combobox": ("Literata", 8),
"sort_combobox": ("Literata", 8), # Added for sorting
}
def config_colors(self):
"""Configure app colors."""
self.colors = {
"text": "#333333",
"background": "#FFFFFF",
"sidebar": "#F7F8FC",
"button": "#F7F8FC",
"button_selected": "#EAEDEF",
"separator": "#333333",
"header_info": "#999999",
"scrollbar_trough": "#F7F8FC",
"scrollbar_background": "#c0c1c4",
"scrollbar_arrow": "#333333",
"scrollbar_active": "#878789",
"filter_line": "#00bcf0",
}
def setup_ui(self):
"""Set up the user interface with a 3x2 grid layout."""
self.root.grid_rowconfigure(0, weight=0)
self.root.grid_rowconfigure(1, weight=1)
self.root.grid_columnconfigure(0, weight=0)
self.root.grid_columnconfigure(1, weight=0, minsize=self.dimensions["spacer_frame_width"])
self.root.grid_columnconfigure(2, weight=3)
self.create_sidebar()
self.create_header_frame()
self.create_spacer_frame()
self.create_table_frame()
def create_sidebar(self):
"""Create the sidebar that spans both rows."""
sidebar = self._create_frame(
parent=self.root,
width=self.dimensions["sidebar_width"],
bg=self.colors["sidebar"]
)
sidebar.grid(row=0, column=0, rowspan=2, sticky='ns')
sidebar.grid_propagate(False)
sidebar.columnconfigure(0, weight=1)
sidebar.rowconfigure(0, weight=1)
functional_sidebar = self._create_frame(
parent=sidebar,
width=self.dimensions["sidebar_width"],
bg=self.colors["sidebar"]
)
functional_sidebar.grid(row=0, column=0, sticky='nsew')
functional_sidebar.columnconfigure(0, weight=1)
functional_sidebar.rowconfigure(0, weight=0)
functional_sidebar.rowconfigure(1, weight=1)
functional_sidebar.rowconfigure(2, weight=0)
self._add_normal_buttons(functional_sidebar)
self._add_scrollable_canvas(functional_sidebar)
self._add_delete_table_section(functional_sidebar)
def create_header_frame(self) -> None:
"""Create the header divided into upper and lower sub-frames."""
header_frame = self._create_and_configure_header_frame()
upper_header_frame = self._create_upper_header(header_frame)
self._create_upper_header_content(upper_header_frame)
lower_header_frame = self._create_lower_header(header_frame)
self._create_filter_controls(lower_header_frame)
self._create_sort_controls(lower_header_frame) # Added: Create sort controls
def _create_and_configure_header_frame(self) -> tk.Frame:
"""Create and configure the main header frame."""
header_frame = self._create_frame(parent=self.root, width=None, bg=self.colors["background"])
header_frame.grid(row=0, column=1, columnspan=2, sticky='ew')
header_frame.config(height=self.dimensions["header_height"])
header_frame.grid_propagate(False)
for i in range(2):
header_frame.grid_rowconfigure(i, weight=0)
for i in range(5):
weight = 1 if i == 4 else 0
header_frame.grid_columnconfigure(i, weight=weight)
return header_frame
def _create_upper_header(self, parent: tk.Frame) -> tk.Frame:
"""Create the upper header frame."""
upper_header = self._create_frame(parent=parent, width=None, bg=self.colors["background"])
upper_header.grid(row=0, column=0, columnspan=5, sticky='ew', pady=(0, 10))
upper_header.grid_columnconfigure(5, weight=1)
return upper_header
def _create_upper_header_content(self, upper_header: tk.Frame) -> None:
"""Create and add content to the upper header frame."""
self._create_data_label(upper_header)
self._create_table_name_label(upper_header)
self._create_settings_label(upper_header)
def _create_data_label(self, parent: tk.Frame) -> None:
"""Create and grid the 'Data >' label."""
data_label = tk.Label(
parent,
text="Data > ",
bg=self.colors["background"],
fg=self.colors["header_info"],
font=self.fonts["header_info"]
)
data_label.grid(row=0, column=0, padx=22, pady=(22, 0), sticky='nw')
def _create_table_name_label(self, parent: tk.Frame) -> None:
"""Create and grid the table name label."""
self.table_name_label = tk.Label(
parent,
text=self.current_table_name if self.current_table_name else "No Table Selected",
bg=self.colors["background"],
fg=self.colors["text"],
font=self.fonts["header_info"]
)
self.table_name_label.grid(row=0, column=0, padx=(88, 10), pady=(22, 0), sticky='nw')
def _create_settings_label(self, parent: tk.Frame) -> None:
"""Create and grid the settings label with its associated menu."""
settings_label = tk.Label(
parent,
text="...",
bg=self.colors["background"],
fg=self.colors["text"],
cursor="hand2",
anchor='e',
font=self.fonts["text"]
)
settings_label.grid(row=0, column=5, padx=40, pady=20, sticky='e')
settings_menu = self._create_settings_menu()
self._bind_settings_label_events(settings_label, settings_menu)
def _create_settings_menu(self) -> tk.Menu:
"""Create the settings dropdown menu."""
menu = tk.Menu(self.root, tearoff=0)
menu.add_command(label="Export Table", command=self.export_table)
menu.add_command(label="Import Table", command=self.import_file)
menu.add_command(label="Rename Headers", command=self.rename_headers)
return menu
def _bind_settings_label_events(self, label: tk.Label, menu: tk.Menu) -> None:
"""Bind events to the settings label."""
label.bind("<Button-1>", lambda e: menu.post(e.x_root, e.y_root))
label.bind("<Enter>", lambda _: label.config(bg=self.colors["button_selected"]))
label.bind("<Leave>", lambda _: label.config(bg=self.colors["background"]))
def _create_lower_header(self, parent: tk.Frame) -> tk.Frame:
"""Create the lower header frame."""
lower_header = self._create_frame(parent=parent, width=None, bg=self.colors["background"])
lower_header.grid(row=1, column=0, columnspan=5, sticky='ew')
return lower_header
def _create_filter_controls(self, parent: tk.Frame) -> None:
"""Create and add filter controls to the lower header frame."""
filter_frame, self.filter_header_dropdown, self.filter_option_dropdown, self.filter_text_entry = custom_filter_design(
parent,
self.style,
headers=["All"] + self.headers.get(self.current_table_name, []),
filter_types=[
"is", "is not", "contains", "does not contain",
"starts with", "ends with", "is empty", "is not empty"
],
filter_font=self.fonts["filter"],
filter_combobox_font=self.fonts["filter_combobox"],
colors=self.colors
)
filter_frame.grid(row=0, column=0, padx=50, pady=0, sticky='ew')
self.filter_header_var = tk.StringVar(value="All")
self.filter_option_var = tk.StringVar(value="contains")
self.filter_text_var = tk.StringVar()
self.filter_header_var.set("All")
self.filter_option_var.set("contains")
self.filter_text_var.set("")
self.filter_header_dropdown['textvariable'] = self.filter_header_var
self.filter_option_dropdown['textvariable'] = self.filter_option_var
self.filter_text_entry['textvariable'] = self.filter_text_var
self.filter_header_dropdown.bind("<<ComboboxSelected>>", self.update_filter_sort_headers)
self.filter_option_dropdown.bind("<<ComboboxSelected>>", lambda event: [self.update_filter_text_state(event), self.update_display()])
self.filter_text_entry.bind("<KeyRelease>", self.update_display)
self.filter_text_entry.bind("<Return>", self.update_display)
def _create_sort_controls(self, parent: tk.Frame) -> None:
"""Create and add sort controls to the lower header frame."""
sort_frame, self.sort_primary_column_dropdown, self.sort_primary_order_dropdown, self.sort_secondary_column_dropdown, self.sort_secondary_order_dropdown = custom_sort_design(
parent,
self.style,
headers=self.headers.get(self.current_table_name, []),
sort_orders=["Ascending", "Descending"],
sort_font=self.fonts["filter"],
sort_combobox_font=self.fonts["sort_combobox"],
colors=self.colors
)
sort_frame.grid(row=0, column=1, padx=50, pady=0, sticky='ew')
# Initialize sort variables
self.sort_primary_column_var = tk.StringVar()
self.sort_primary_order_var = tk.StringVar()
self.sort_secondary_column_var = tk.StringVar()
self.sort_secondary_order_var = tk.StringVar()
self.sort_primary_column_var.set(self.headers[self.current_table_name][0] if self.headers.get(self.current_table_name) else "")
self.sort_primary_order_var.set("Ascending")
self.sort_secondary_column_var.set(self.headers[self.current_table_name][0] if self.headers.get(self.current_table_name) else "")
self.sort_secondary_order_var.set("Ascending")
self.sort_primary_column_dropdown['textvariable'] = self.sort_primary_column_var
self.sort_primary_order_dropdown['textvariable'] = self.sort_primary_order_var
self.sort_secondary_column_dropdown['textvariable'] = self.sort_secondary_column_var
self.sort_secondary_order_dropdown['textvariable'] = self.sort_secondary_order_var
# Bind sorting comboboxes to sort function
self.sort_primary_column_dropdown.bind("<<ComboboxSelected>>", self.update_display)
self.sort_primary_order_dropdown.bind("<<ComboboxSelected>>", self.update_display)
self.sort_secondary_column_dropdown.bind("<<ComboboxSelected>>", self.update_display)
self.sort_secondary_order_dropdown.bind("<<ComboboxSelected>>", self.update_display)
def update_filter_sort_headers(self, event=None):
"""Update filter and sort comboboxes when a new table is selected."""
if not self.current_table_name:
headers = []
else:
headers = self.headers.get(self.current_table_name, [])
# Update filter headers
self.filter_header_dropdown['values'] = ["All"] + headers
self.filter_header_var.set("All")
# Update sort headers
self.sort_primary_column_dropdown['values'] = headers
self.sort_secondary_column_dropdown['values'] = headers
# Reset sort selections
if headers:
self.sort_primary_column_var.set(headers[0])
self.sort_primary_order_var.set("Ascending")
self.sort_secondary_column_var.set(headers[0])
self.sort_secondary_order_var.set("Ascending")
else:
self.sort_primary_column_var.set("")
self.sort_primary_order_var.set("")
self.sort_secondary_column_var.set("")
self.sort_secondary_order_var.set("")
self.update_display()
def update_display(self):
"""Apply filter and sort, then update the sheet."""
self.apply_filter()
self.apply_sort()
def apply_filter(self, event=None):
"""Apply filter to table data based on selected criteria."""
if not self.current_table_name or not self.current_sheet:
return
header = self.filter_header_var.get()
option = self.filter_option_var.get()
text = self.filter_text_var.get().lower()
original_data = self.tables_data[self.current_table_name]["data"]
def row_matches(row):
if header == "All":
cells = row
target_indices = range(len(cells))
else:
try:
target_indices = [self.headers[self.current_table_name].index(header)]
except ValueError:
return False
conditions = {
"is": lambda cell, text: cell == text,
"is not": lambda cell, text: cell != text,
"contains": lambda cell, text: text in cell,
"does not contain": lambda cell, text: text not in cell,
"starts with": lambda cell, text: cell.startswith(text),
"ends with": lambda cell, text: cell.endswith(text),
"is empty": lambda cell, _: cell == "",
"is not empty": lambda cell, _: cell != ""
}
for idx in target_indices:
cell = str(row[idx]).lower()
if conditions.get(option, lambda _, __: False)(cell, text):
return True
return False
# Collect both filtered data and their original indices
filtered_data = []
self.displayed_row_indices = []
for i, row in enumerate(original_data):
if row_matches(row):
filtered_data.append(row)
self.displayed_row_indices.append(i)
try:
self.current_sheet.set_sheet_data(filtered_data)
except tk.TclError as e:
print(f"Error applying filter: {e}")
def apply_sort(self):
"""Sort the filtered data based on selected sort options."""
if not self.current_table_name or not self.current_sheet:
return
headers = self.headers.get(self.current_table_name, [])
if not headers:
return
primary_column = self.sort_primary_column_var.get()
primary_order = self.sort_primary_order_var.get()
secondary_column = self.sort_secondary_column_var.get()
secondary_order = self.sort_secondary_order_var.get()
if not primary_column:
return # No sorting applied
try:
primary_index = headers.index(primary_column)
except ValueError:
primary_index = -1
try:
secondary_index = headers.index(secondary_column)
except ValueError:
secondary_index = -1
# Extract the filtered data based on displayed_row_indices
filtered_data = [self.tables_data[self.current_table_name]["data"][i] for i in self.displayed_row_indices]
# Define sort key
def sort_key(row):
primary_value = self.get_sortable_value(row[primary_index]) if primary_index != -1 else ""
secondary_value = self.get_sortable_value(row[secondary_index]) if secondary_index != -1 else ""
return (primary_value, secondary_value)
# Determine sort order
reverse_primary = True if primary_order == "Descending" else False
reverse_secondary = True if secondary_order == "Descending" else False
# Sort the data
filtered_data.sort(key=sort_key, reverse=reverse_primary) # Sort primarily
# For secondary sort, we need to sort again to maintain stability
if secondary_column != primary_column:
filtered_data.sort(key=lambda row: self.get_sortable_value(row[secondary_index]) if secondary_index != -1 else "", reverse=reverse_secondary)
# Update the sheet with sorted data
try:
self.current_sheet.set_sheet_data(filtered_data)
except tk.TclError as e:
print(f"Error applying sort: {e}")
def get_sortable_value(self, value):
"""Convert value to a sortable format."""
try:
return float(value)
except ValueError:
return value.lower()
def create_spacer_frame(self):
"""Create a spacer frame in column 1, row 1."""
spacer = self._create_frame(
parent=self.root,
width=self.dimensions["spacer_frame_width"],
bg=self.colors["background"]
)
spacer.grid(row=1, column=1, sticky='nsew')
spacer.grid_propagate(False)
def create_table_frame(self):
"""Create the table frame in column 2, row 1."""
self.table_frame = self._create_frame(
parent=self.root,
width=None,
bg=self.colors["background"]
)
self.table_frame.grid(row=1, column=2, sticky='nsew')
self.table_frame.grid_rowconfigure(0, weight=1)
self.table_frame.grid_columnconfigure(0, weight=1)
def _create_frame(
self,
parent: tk.Widget,
width: Optional[int],
bg: str
) -> tk.Frame:
"""Create a frame with specified width and background."""
frame = tk.Frame(parent, width=width, bg=bg, bd=0, relief='flat')
if width:
frame.config(width=width)
return frame
def _add_normal_buttons(self, sidebar: tk.Frame):
"""Add buttons to the sidebar."""
normal_buttons_frame = self._create_frame(
parent=sidebar,
width=self.dimensions["sidebar_width"],
bg=self.colors["sidebar"]
)
normal_buttons_frame.grid(row=0, column=0, sticky='ew', padx=10, pady=(10, 0))
normal_buttons_frame.columnconfigure(0, weight=1)
create_table_label = self._create_clickable_label(
parent=normal_buttons_frame,
text="Create New Table",
command=self.create_new_table,
anchor='center'
)
create_table_label.grid(row=0, column=0, pady=10, sticky='ew')
self.add_custom_hover(create_table_label)
separator = tk.Frame(normal_buttons_frame, height=self.dimensions["separator_height"], bg=self.colors["separator"])
separator.grid(row=1, column=0, sticky='ew', pady=5)
def _on_canvas_configure(self, event):
"""Update the table_labels_frame width to match the canvas width."""
self.canvas.itemconfig(self.table_labels_window, width=event.width)
def _add_scrollable_canvas(self, sidebar: tk.Frame):
"""Add scrollable canvas for table labels within a single frame."""
scrollable_container = self._create_frame(
parent=sidebar,
width=self.dimensions["sidebar_width"],
bg=self.colors["sidebar"]
)
scrollable_container.grid(row=1, column=0, sticky='nsew', padx=10, pady=10)
scrollable_container.columnconfigure(0, weight=1)
scrollable_container.rowconfigure(0, weight=1)
self.canvas = tk.Canvas(
scrollable_container,
bg=self.colors["sidebar"],
highlightthickness=0
)
self.canvas.grid(row=0, column=0, sticky='nsew')
custom_scrollbar_design(self.style, self.colors)
self.scrollbar = ttk.Scrollbar(
scrollable_container,
orient="vertical",
command=self.canvas.yview,
style="Custom.Vertical.TScrollbar"
)
self.scrollbar.grid(row=0, column=1, sticky='ns')
self.canvas.configure(yscrollcommand=self.scrollbar.set)
self.table_labels_frame = tk.Frame(self.canvas, bg=self.colors["sidebar"])
# Capture the window item ID
self.table_labels_window = self.canvas.create_window((0, 0), window=self.table_labels_frame, anchor="nw", width=self.dimensions["sidebar_width"])
self.canvas.bind("<MouseWheel>", self._on_mouse_wheel)
self.table_labels_frame.bind("<MouseWheel>", self._on_mouse_wheel)
self.table_labels_frame.bind(
"<Configure>",
lambda event: (
self.canvas.configure(scrollregion=self.canvas.bbox("all")),
self._update_scrollbar(scrollable_container, self.canvas, self.scrollbar)
)
)
scrollable_container.bind(
"<Configure>",
lambda event: self._update_scrollbar(scrollable_container, self.canvas, self.scrollbar)
)
# Bind the canvas resize to update the table_labels_frame width
self.canvas.bind('<Configure>', self._on_canvas_configure)
def _on_mouse_wheel(self, event):
"""Handle mouse wheel scrolling only if scrollbar is visible."""
if self.scrollbar.winfo_viewable():
self.canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
def _update_scrollbar(self, scrollbar_frame, canvas, scrollbar):
"""Update scrollbar visibility based on content size."""
canvas.update_idletasks()
canvas_height = scrollbar_frame.winfo_height()
content_height = self.table_labels_frame.winfo_reqheight()
if content_height > canvas_height:
scrollbar.grid()
canvas.configure(yscrollcommand=scrollbar.set)
canvas.bind("<MouseWheel>", lambda event: canvas.yview_scroll(int(-1 * (event.delta / 120)), "units"))
else:
scrollbar.grid_remove()
canvas.configure(yscrollcommand=lambda *args: None)
canvas.unbind("<MouseWheel>")
def _add_delete_table_section(self, sidebar: tk.Frame):
"""Add delete table option to the sidebar."""
delete_table_frame = self._create_frame(
parent=sidebar,
width=self.dimensions["sidebar_width"],
bg=self.colors["sidebar"]
)
delete_table_frame.grid(row=2, column=0, sticky='ew', padx=10, pady=(0, 10))
delete_table_frame.columnconfigure(0, weight=1)
delete_table_frame.rowconfigure(1, weight=1)
separator = tk.Frame(delete_table_frame, height=self.dimensions["separator_height"], bg=self.colors["separator"])
separator.grid(row=0, column=0, sticky='ew', pady=5)
delete_table_label = self._create_clickable_label(
parent=delete_table_frame,
text="🗑 Delete Selected Table",
command=self.delete_table,
anchor='center'
)
delete_table_label.grid(row=1, column=0, pady=10, sticky='ew')
self.add_custom_hover(delete_table_label)
def _create_clickable_label(
self,
parent: tk.Widget,
text: str,
command,
allow_rename: bool = False,
anchor: str = 'w'
) -> tk.Label:
"""Create a clickable label with optional rename functionality."""
cursor = "arrow" if allow_rename else "hand2"
label = tk.Label(
parent,
text=text,
bg=self.colors["button"],
fg=self.colors["text"],
cursor=cursor,
anchor=anchor,
font=self.fonts["text"]
)
label.bind("<Button-1>", lambda _: command())
if allow_rename:
label.bind("<Double-Button-1>", lambda _: self.begin_rename(text))
label.bind("<MouseWheel>", self._on_mouse_wheel)
return label
def add_custom_hover(self, widget: tk.Widget):
"""Add custom hover effects to a widget."""
widget.bind("<Enter>", lambda e: widget.config(bg=self.colors["button_selected"]))
widget.bind("<Leave>", lambda e: widget.config(bg=self.colors["button"]))
def initialize_data(self):
"""Initialize data structures."""
self.save_file_path = Path(__file__).parent / "Data.json"
self.tables_data: dict[str, dict] = {}
self.current_sheet: Sheet | None = None
self.headers: dict[str, list[str]] = {}
self.current_table_name: str | None = None
self.rename_entry: tk.Entry | None = None
self.selected_table_label: tk.Label | None = None
self.table_labels: dict[str, tk.Label] = {}
self.table_name_label = None
self.filter_header_var = tk.StringVar(value="All")
self.filter_option_var = tk.StringVar(value="contains")
self.filter_text_var = tk.StringVar()
# Sorting variables
self.sort_primary_column_var = tk.StringVar()
self.sort_primary_order_var = tk.StringVar()
self.sort_secondary_column_var = tk.StringVar()
self.sort_secondary_order_var = tk.StringVar()
# Initialize displayed_row_indices
self.displayed_row_indices: list[int] = []
def sort_table_labels(self):
"""Sort table labels with numeric names first, then alphabetic, then alphanumeric."""
def sort_key(name):
# Category 1: Starts with digits (e.g., "1. Movies")
m = re.match(r'^(\d+)', name)
if m:
return (1, int(m.group(1)))
# Category 3: Starts with letters followed by digits (e.g., "T1", "T2")
m = re.match(r'^([A-Za-z]+)(\d+)', name)
if m:
return (3, m.group(1).lower(), int(m.group(2)))
# Category 2: Starts with letters only (e.g., "a", "b", "c")
m = re.match(r'^([A-Za-z]+)', name)
if m:
return (2, m.group(1).lower())
# Category 4: Any other names
return (4, name.lower())
# Sort the table names using the defined sort key
sorted_tables = sorted(self.tables_data.keys(), key=sort_key)
# Re-pack the labels in the sorted order
for table_name in sorted_tables:
label = self.table_labels.get(table_name)
if label:
label.pack_forget()
label.pack(fill='x', padx=0, pady=5, expand=True)
def create_new_table(self):
"""Create a new table."""
existing_numbers = [int(name.split(" ")[1]) for name in self.tables_data if name.startswith("Table ") and name.split(" ")[1].isdigit()]
next_number = max(existing_numbers, default=0) + 1
table_name = f"Table {next_number}"
self.add_table(table_name)
self.load_selected_table(table_name)
def import_file(self):
"""Import table from file."""
file_path = filedialog.askopenfilename(title="Import File", filetypes=[("All Supported Files", "*.json *.csv"), ("JSON files", "*.json"), ("CSV files", "*.csv")])
if not file_path:
return
try:
if file_path.endswith('.csv'):
with open(file_path, newline='', encoding='utf-8') as csvfile:
data = list(csv.reader(csvfile))
elif file_path.endswith('.json'):
with open(file_path, 'r', encoding='utf-8') as jsonfile:
data = json.load(jsonfile)
else:
raise ValueError("Unsupported file format.")
if not data or not isinstance(data, list) or not all(isinstance(row, list) for row in data):
raise ValueError("Invalid data format in the file.")
headers, table_data = data[0], data[1:]
self.add_table_from_data(file_path, headers, table_data)
except Exception as e:
messagebox.showerror("File Import Error", str(e))
def add_table_from_data(self, file_path: str, headers: list[str], table_data: list[list[str]]):
"""Add a table from file data."""
table_name = Path(file_path).stem
table_name = self.ensure_unique_table_name(table_name)
self.add_table(table_name, data=table_data, headers=headers)
self.load_selected_table(table_name)
self.save_data()
def ensure_unique_table_name(self, table_name: str) -> str:
"""Ensure table name is unique."""
original_name = table_name
counter = 1
while table_name in self.tables_data:
table_name = f"{original_name} (New {counter})"
counter += 1
return table_name
def save_table_data(self, file_path: str, data: list[list[str]], format: str):
"""Save table data in specified format."""
match format:
case 'csv':
with open(file_path, 'w', newline='', encoding='utf-8') as file:
csv.writer(file).writerows(data)
case 'json':
with open(file_path, 'w', encoding='utf-8') as file:
json.dump(data, file, indent=4)
case _:
raise ValueError("Unsupported file format.")
def export_table(self):
"""Export current table to file."""
if not self.current_table_name or self.current_table_name not in self.tables_data:
messagebox.showwarning("No Table Selected", "Please select a table to export.")
return
file_path = filedialog.asksaveasfilename(title="Export File", defaultextension=".json", filetypes=[("JSON files", "*.json"), ("CSV files", "*.csv")], initialfile=self.current_table_name)
if not file_path:
return
try:
data = [self.headers[self.current_table_name]] + self.tables_data[self.current_table_name]["data"]
if file_path.endswith('.csv'):
self.save_table_data(file_path, data, 'csv')
elif file_path.endswith('.json'):
with open(file_path, 'w', encoding='utf-8') as file:
json.dump(data, file, indent=4)
else:
raise ValueError("Unsupported file format.")
self.save_data()
messagebox.showinfo("Export Successful", f"Table '{self.current_table_name}' has been exported successfully.")
except Exception as e:
messagebox.showerror("Export Failed", f"An error occurred during export: {e}")
def add_table(self, table_name: str, data: list[list[str]] | None = None, headers: Optional[list[str]] = None):
"""Add a new table."""
if headers:
self.headers[table_name] = headers
else:
default_headers = ["Column 1", "Column 2", "Column 3", "Column 4", "Column 5"]
self.headers[table_name] = default_headers
self.tables_data[table_name] = {"headers": self.headers[table_name], "data": data or [["" for _ in range(len(self.headers[table_name]))] for _ in range(10)]}
self.create_table_label(table_name)
self.save_data()
def create_table_label(self, table_name: str):
"""Create label for table."""
label = self._create_clickable_label(self.table_labels_frame, table_name, lambda: self.load_selected_table(table_name), True)
label.pack(fill='x', padx=0, pady=5, expand=True)
self.table_labels[table_name] = label
self.add_custom_hover(label)
self.sort_table_labels()
def delete_table(self):
"""Delete selected table."""
if not self.current_table_name or self.current_table_name not in self.tables_data:
messagebox.showwarning("No Table Selected", "Please select a valid table to delete.")
return
if messagebox.askyesno("Delete Table", f"Are you sure you want to delete the table '{self.current_table_name}'?"):
self.remove_table(self.current_table_name)
def on_columns_deleted(self, event):
"""Handle column deletion and update filter headers."""
if not self.current_table_name or not self.current_sheet:
return
new_headers = self.current_sheet.headers()
self.headers[self.current_table_name] = new_headers
self.filter_header_dropdown['values'] = ["All"] + new_headers
self.filter_header_var.set("All")
self.filter_option_var.set("contains")
self.filter_text_var.set("")
self.filter_text_entry.config(state='normal')
self.update_filter_sort_headers()
self.update_display()
def remove_table(self, table_name: str):
"""Remove a table."""
if self.current_table_name == table_name and self.current_sheet:
self.current_sheet.pack_forget()
self.current_sheet.destroy()
self.current_sheet = None
label = self.table_labels.get(table_name)
if label:
label.destroy()
if table_name in self.tables_data:
del self.tables_data[table_name]
if table_name in self.headers:
del self.headers[table_name]
if table_name in self.table_labels:
del self.table_labels[table_name]
self.current_table_name = None
self.selected_table_label = None
self.save_data()
self.sort_table_labels()
def load_tables(self):
"""Load tables from file."""
if not self.save_file_path.exists():
return
try:
with self.save_file_path.open('r', encoding='utf-8') as file:
all_data = json.load(file)
except Exception as e:
messagebox.showerror("Load Error", f"Failed to load tables: {e}")
return
for table_name, data in all_data.items():
if not isinstance(data, list) or not data:
continue
headers, table_data = data[0], data[1:]
self.headers[table_name] = headers
self.tables_data[table_name] = {"headers": headers, "data": table_data}
self.create_table_label(table_name)
self.sort_table_labels()
def load_selected_table(self, table_name: str):
"""Display selected table."""
if self.current_sheet:
self.current_sheet.pack_forget()
self.current_sheet.destroy()
self.current_sheet = None
if self.selected_table_label:
try:
self.selected_table_label.config(bg=self.colors["button"])
except tk.TclError:
self.selected_table_label = None
if table_name not in self.tables_data:
messagebox.showerror("Error", f"Table '{table_name}' does not exist.")
self.current_table_name = None
self.selected_table_label = None
return
table_info = self.tables_data[table_name]
headers = table_info["headers"]
data = table_info["data"]
try:
sheet = Sheet(
self.table_frame,
data=data,
show_table=True,
headers=headers,
font=self.fonts["tksheet"],
header_font=self.fonts["tksheet"],
auto_resize_columns=True
)
sheet.set_options(table_fg=self.colors["text"], header_fg=self.colors["text"])
sheet.enable_bindings()
sheet.extra_bindings([
("end_edit_cell", self.on_cell_edit),
("end_delete_columns", self.on_columns_deleted) # Added binding for column deletion
])
sheet.pack(expand=True, fill='both')
self.current_sheet = sheet
self.current_table_name = table_name
if self.table_name_label:
self.table_name_label.config(text=self.current_table_name)
else:
self.table_name_label = tk.Label(
self.root,
text=self.current_table_name,
bg=self.colors["background"],
fg=self.colors["text"],
font=self.fonts["header_info"]
)
self.table_name_label.grid(row=0, column=0, padx=(60, 10), pady=(5, 0), sticky='nw')
label = self.table_labels.get(table_name)
if label:
self.selected_table_label = label
try:
self.selected_table_label.config(bg=self.colors["button_selected"])
except tk.TclError:
self.selected_table_label = None
messagebox.showerror("Error", f"Failed to update the label for '{table_name}'.")
else:
self.selected_table_label = None
messagebox.showerror("Error", f"Label for '{table_name}' not found.")
self.update_filter_sort_headers()
except Exception as e:
messagebox.showerror("Sheet Error", f"Failed to load table '{table_name}': {e}")
self.current_table_name = None
self.selected_table_label = None
def on_cell_edit(self, event=None):
"""Handle cell edits."""
if self.current_table_name and self.current_sheet:
sheet_data = self.current_sheet.get_sheet_data()
for idx, row in enumerate(sheet_data):
if idx < len(self.displayed_row_indices):
data_idx = self.displayed_row_indices[idx]
self.tables_data[self.current_table_name]["data"][data_idx] = row
else:
# Handle new rows by appending to the underlying data
self.tables_data[self.current_table_name]["data"].append(row)
self.save_data()
def save_data(self):
"""Save all tables data."""
all_data = {}
for table_name, table_info in self.tables_data.items():
all_data[table_name] = [table_info["headers"]] + table_info["data"]
try:
with self.save_file_path.open('w', encoding='utf-8') as file:
json.dump(all_data, file, indent=4)
except Exception as e:
print(f"Error saving data: {e}")
def apply_filter(self, event=None):
"""Apply filter to table data based on selected criteria."""
if not self.current_table_name or not self.current_sheet:
return
header = self.filter_header_var.get()
option = self.filter_option_var.get()
text = self.filter_text_var.get().lower()
original_data = self.tables_data[self.current_table_name]["data"]
def row_matches(row):
if header == "All":
cells = row
target_indices = range(len(cells))
else:
try:
target_indices = [self.headers[self.current_table_name].index(header)]
except ValueError:
return False
conditions = {
"is": lambda cell, text: cell == text,
"is not": lambda cell, text: cell != text,
"contains": lambda cell, text: text in cell,
"does not contain": lambda cell, text: text not in cell,
"starts with": lambda cell, text: cell.startswith(text),
"ends with": lambda cell, text: cell.endswith(text),
"is empty": lambda cell, _: cell == "",
"is not empty": lambda cell, _: cell != ""
}
for idx in target_indices:
cell = str(row[idx]).lower()
if conditions.get(option, lambda _, __: False)(cell, text):
return True
return False
# Collect both filtered data and their original indices
filtered_data = []
self.displayed_row_indices = []
for i, row in enumerate(original_data):
if row_matches(row):
filtered_data.append(row)
self.displayed_row_indices.append(i)
try:
self.current_sheet.set_sheet_data(filtered_data)
except tk.TclError as e:
print(f"Error applying filter: {e}")
def rename_headers(self):
"""Rename table headers."""
if not self.current_table_name:
messagebox.showwarning("No Table Selected", "Please select a table to rename headers.")
return
current_headers = self.headers[self.current_table_name]
new_headers = simpledialog.askstring(
"Rename Headers",
"Enter new headers (comma-separated):",
initialvalue=", ".join(current_headers)
)
if new_headers:
header_list = [header.strip() for header in new_headers.split(',')]
if not header_list:
messagebox.showerror("Invalid Headers", "Header list cannot be empty.")
return
self.headers[self.current_table_name] = header_list
self.tables_data[self.current_table_name]["headers"] = header_list
if self.current_sheet:
try:
self.current_sheet.headers(header_list)
except tk.TclError as e:
self.headers[self.current_table_name] = current_headers # Revert on error
messagebox.showerror("Error", f"Failed to rename headers: {e}")
return
self.update_filter_sort_headers()
self.save_data()
def begin_rename(self, table_name: str):
"""Start renaming a table."""
if table_name not in self.table_labels:
messagebox.showerror("Error", f"Table '{table_name}' not found.")
return
label = self.table_labels[table_name]
self.cancel_rename()
self.rename_entry = tk.Entry(self.table_labels_frame, bg=label.cget("bg"), fg=self.colors["text"], relief='flat')
self.rename_entry.insert(0, table_name)
self.rename_entry.place(x=label.winfo_x(), y=label.winfo_y(), width=label.winfo_width(), height=label.winfo_height())
self.rename_entry.focus()
self.rename_entry.select_range(0, tk.END)
self.rename_entry.bind('<Return>', lambda event: self.end_rename(table_name))
self.rename_entry.bind('<FocusOut>', lambda event: self.cancel_rename())
def end_rename(self, old_name: str):
"""Finalize table renaming."""
new_name = self.rename_entry.get().strip()
if not new_name:
messagebox.showerror("Invalid Name", "Table name cannot be empty.")
self.rename_entry.focus_set()
return
if new_name in self.tables_data and new_name != old_name:
messagebox.showerror("Duplicate Name", f"A table named '{new_name}' already exists.")
self.rename_entry.focus_set()
return
if new_name != old_name:
self.update_table_name(old_name, new_name)
self.load_selected_table(new_name)
self.cancel_rename()
def update_table_name(self, old_name: str, new_name: str):
"""Update table name and resources."""
self.tables_data[new_name] = self.tables_data.pop(old_name)
self.headers[new_name] = self.headers.pop(old_name)
label = self.table_labels.pop(old_name)
label.config(text=new_name)
label.bind("<Button-1>", lambda event: self.load_selected_table(new_name))
label.bind("<Double-Button-1>", lambda event: self.begin_rename(new_name))
self.table_labels[new_name] = label
if self.selected_table_label == label:
try:
label.config(bg=self.colors["button_selected"])
except tk.TclError:
pass
self.save_data()
self.sort_table_labels()
def cancel_rename(self):
"""Cancel rename operation."""
if self.rename_entry:
self.rename_entry.destroy()
self.rename_entry = None
def update_table_order(self):
"""Sort the table labels."""
self.sort_table_labels()
def add_custom_hover(self, label: tk.Label):
"""Add hover effects to label."""
def on_enter(event):
if label != self.selected_table_label:
try:
label.config(bg=self.colors["button_selected"])
except tk.TclError:
pass
def on_leave(event):
if label != self.selected_table_label:
try:
label.config(bg=self.colors["button"])
except tk.TclError:
pass
label.bind("<Enter>", on_enter)
label.bind("<Leave>", on_leave)
if __name__ == "__main__":
set_dpi_awareness()
root = tk.Tk()
app = TableApp(root)
root.mainloop()
```
### **Key Modifications and Additions**
1. **Custom Sort Design Function**:
- **Function**: `custom_sort_design`
- **Purpose**: Creates a sort frame with comboboxes for selecting primary and secondary sort columns and their respective sort orders.
- **Usage**: Placed next to the filter controls in the header frame.
2. **Integration in Header Frame**:
- **Method**: `_create_sort_controls`
- **Purpose**: Initializes the sort controls and binds them to the sorting functionality.
- **Placement**: Called within `create_header_frame` to position sort controls adjacent to filter controls.
3. **Dynamic Updating of Sort Options**:
- **Method**: `update_filter_sort_headers`
- **Purpose**: Updates both filter and sort comboboxes whenever a new table is selected or headers are renamed.
- **Binding**: Called in `load_selected_table` and `on_columns_deleted` to ensure sort options are in sync with the current headers.
4. **Applying Sorting in Conjunction with Filtering**:
- **Method**: `update_display`
- **Purpose**: Calls both `apply_filter` and `apply_sort` to ensure that sorting is applied after filtering.
- **Usage**: Triggered whenever filter or sort options change.
- **Method**: `apply_sort`
- **Purpose**: Sorts the filtered data based on selected primary and secondary sort options.
- **Implementation**: Handles both ascending and descending orders and ensures secondary sorting when necessary.
5. **Handling Headers Update**:
- Whenever headers are renamed or modified, the sort combobox options are automatically updated to reflect the current headers, ensuring that users can sort based on the latest column names.
6. **Additional Variables**:
- Added sorting-related variables like `self.sort_primary_column_var`, `self.sort_primary_order_var`, etc., to track the user's sort selections.
### **Usage Notes**
- **Filter and Sort Side by Side**: The filter controls are now placed next to the sort controls in the header frame, providing an intuitive UI for users to filter and sort data seamlessly.
- **Dynamic Dropdowns**: Both filter and sort comboboxes update their options based on the current table's headers, ensuring that users always have relevant sorting and filtering options.
- **Combined Display Update**: Whenever a filter or sort option is changed, the `update_display` method ensures that the table view reflects both the filtering and sorting criteria.
This integration ensures that the sorting functionality works harmoniously with the existing filtering mechanism, providing a robust and dynamic user experience.9785624c22ea15f897256264Cessna 172'nin seyir uçuşu halindeyken boylamsal dengesini hesapla ve doğrula.
Cessna 172'nin seyir uçuşu (cruise flight) esnasındaki boylamsal (longitudinal) dengesi, uçağın stabilizasyonunu ve kontrol edilebilirliğini sağlamak için kritik bir faktördür. Boylamsal denge, uçağın ağırlık merkezinin (Center of Gravity - CG) kaldırma kuvvetine (lift) ve arka ucunda bulunan kuyruk yüzeylerinin yarattığı momentlerle dengede tutulması prensibine dayanır.
Aşağıda Cessna 172'nin boylamsal dengesini hesaplamak ve doğrulamak için genel adımları ve gereken temel parametreleri bulabilirsiniz. Ancak, spesifik hesaplamalar için kesin ağırlık dağılımı, CG aralığı, kaldırma kuvveti koefisientleri gibi detaylı verilere ihtiyaç vardır. Bu nedenle, örnek bir hesaplama üzerinden genel bir yaklaşım sunacağım.
### Gerekli Parametreler
1. **Uçağın Toplam Ağırlığı (W_total):** Seyir uçuşundaki toplam ağırlık.
2. **Ağırlık Merkezi (CG) Konumu:** Uçağın ağırlık merkezinin referans noktasına göre konumu.
3. **Kaldırma Kuvveti Anı (Lift Moment):** Kanatların uçağın CG'sine oranla ürettiği kaldırma kuvvetinin momenti.
4. **Kuyruk Yüzeylerinden Gelen Arm:** Kuyruk yüzeylerinin CG'ye göre konumu.
5. **Kuyruk Yüzey Kuvvetleri:** Kuyruk tarafından sağlanan dengeleyici kuvvetler.
### Genel Hesaplama Adımları
1. **Ağırlık Merkezi (CG) Hesaplaması:**
\[
CG = \frac{\sum (Ağırlık \times Mesafe)}{\sum Ağırlık}
\]
Bu formül, tüm ağırlık bileşenlerinin mesafelerine göre ağırlık merkezini belirler.
2. **Kaldırma Kuvveti Momentinin Hesaplanması:**
\[
Moment_{lift} = W_{total} \times d_{CG}
\]
Burada \(d_{CG}\), CG'nin rota eksenine göre mesafesidir.
3. **Kuyruk Kuvveti Momentinin Hesaplanması:**
Kuyruk yüzeyleri, uçağın stabilizasyonu için gerekli olan momenti üretir. Bu momentin, kaldırma momentini dengelemesi gerekir.
\[
Moment_{tail} = L_{tail} \times d_{tail}
\]
Burada \(L_{tail}\), kuyruk yüzeylerinin ürettiği kaldırma kuvveti ve \(d_{tail}\) ise kuyruk yüzeylerinin CG'ye göre mesafesidir.
4. **Denge Koşulu:**
Uçağın boylamsal dengesini sağlamak için:
\[
Moment_{lift} = Moment_{tail}
\]
### Örnek Hesaplama
Farzedelim ki:
- **Toplam Ağırlık (W_total):** 750 kg
- **CG Mesafesi (d_CG):** 2.0 m (rota ucundan geride)
- **Kuyruk Kaldırma Kuvveti (L_tail):** 1200 N (1 N ≈ 0.10197 kg, yaklaşık 120 kg)
- **Kuyruk Mesafesi (d_tail):** 5.0 m (rota ucundan önde)
**Adımlar:**
1. **Moment Lift:**
\[
Moment_{lift} = 750 \, kg \times 2.0 \, m = 1500 \, kg·m
\]
2. **Moment Tail:**
\[
Moment_{tail} = 120 \, kg \times 5.0 \, m = 600 \, kg·m
\]
3. **Denge Durumu:**
\[
1500 \, kg·m ≠ 600 \, kg·m
\]
Bu durumda, denge sağlanmamıştır. Uçağın boylamsal dengesini sağlamak için ya CG'yi geriye (rota ucuna) kaydırmak ya da kuyruk kuvvetini artırmak gerekir.
### Doğrulama
Boylamsal denge, hesaplamaların ardından yapılan test uçuşları ve simülasyonlarla doğrulanır. Simülasyonlar, teorik hesaplamaların uçağın gerçek davranışıyla uyumlu olup olmadığını kontrol eder. Ayrıca, CG aralığının üretici tarafından belirlenen sınırlamalar içinde kalması sağlanmalıdır.
### Cessna 172 İçin Özel Bilgiler
Cessna 172'nin teknik özelliklerine göre:
- **Maksimum Ağırlık:** Genellikle 1,111 kg (2,450 lb)
- **CG Aralığı:** Belirli bir mesafe aralığında (örneğin, 0.375 ile 0.525 la karşılaştırılması gereken)
- **Kuyruk Yüzeyi Kupresi:** Stabilizasyon için optimize edilmiştir.
Ancak, spesifik seyir uçuşu koşulları için tam ve doğru hesaplama yapmak amacıyla üretici el kitabında belirtilen verilerle çalışmanız önemlidir.
### Sonuç
Boylamsal denge, uçağın güvenli ve stabil bir şekilde seyir uçuşu yapabilmesi için kritik öneme sahiptir. Yukarıdaki genel hesaplama yöntemi, bu dengeyi sağlamak için nasıl bir yaklaşım izleneceğini göstermektedir. Ancak, kesin ve güvenilir sonuçlar için Cessna 172'nin teknik dökümanlarına ve spesifik uçuş verilerine başvurmanız gerekmektedir.
Eğer daha detaylı veya spesifik hesaplamalar yapmak istiyorsanız, lütfen aşağıdaki bilgileri sağlayın:
- Uçağın kesin ağırlık dağılımı
- CG referans noktası
- Kullandığınız aerodinamik veriler
- Seyir uçuşundaki spesifik koşullar (hız, irtifa vb.)
Bu bilgilerle daha ayrıntılı bir hesaplama yapmak mümkündür.93f6a248159e0762b95511a5Write me a new movie of Focus Features (Movie Title: Skeleton Duty) (Credits included) (Running Time: 1 hour) (Website:)
**Movie Title:** Skeleton Duty **Genre:** Dark Comedy / Adventure **Running Time:** 1 hour **Website:** [www.skeletondutymovie.com](http://www.skeletondutymovie.com) --- ### Plot Summary: In the small, peculiar town of Bonelake, every fall brings the much-anticipated "Skeleton Duty," a quirky tradition where locals take turns dressing as skeletons and parading around town to ward off mythical creatures believed to roam nearby woods. This year marks the duty of Max, an awkward but well-meaning high school student, and his best friend Lila, an aspiring journalist eager for an adventure that could make her journalism career burst into life. As Max reluctantly dons the dusty old skeleton costume, he quickly realizes this year's duty is far from ordinary. Shortly into their haunting procession, Max and Lila stumble upon a cryptic message hidden within the fabric of his skeleton outfit, leading them on a mysterious trail through Bonelake's long-forgotten history. They uncover a hidden chapter of the town’s past involving a legendary treasure, eager locals who nurtured old grudges, and an eccentric inventor from a forgotten time. As secrets unfurl, Max and Lila enlist the help of Mr. Gibbles, the town's witty and enigmatic librarian who knows more about Bonelake's history than anyone else. The trio embarks on a whirlwind adventure across the town’s haunted landmarks, solving riddles and facing challenges only those brave enough dare to confront. But they are not alone on this quest. Geraldine Grimsby, the town's overzealous history enthusiast and Lila's fierce rival, becomes aware of the hunt and is determined to claim the treasure's glory for herself, concocting plans that lead to comedic stand-offs and unexpected twists. As the hour ticks by and Halloween night draws nearer, Max must learn to embrace his inner courage, Lila must decide what true journalism means to her, and together they must protect the legacy of Bonelake. Skeleton Duty becomes not just a parade, but a legacy, uniting the town in a comedy of errors where past and present collide. --- ### Cast: - **Max Turner** - Played by *Finn Wolfhard* - **Lila Ramirez** - Played by *Maitreyi Ramakrishnan* - **Mr. Gibbles** - Played by *Richard E. Grant* - **Geraldine Grimsby** - Played by *Helena Bonham Carter* ### Director: - *Armando Iannucci* ### Screenplay: - *Phoebe Waller-Bridge* ### Producers: - *Tim Bevan* - *Eric Fellner* ### Cinematography: - *Seamus McGarvey* ### Music: - *Ludwig Göransson* ### Production Company: - *Focus Features* --- **Visit [www.skeletondutymovie.com](http://www.skeletondutymovie.com) for trailers, behind-the-scenes content, and more about the cast and filmmakers. Dive into the quirky world of Bonelake and uncover the mystery of the Skeleton Duty!**
a5da67d637606756c4377156что такое "Netflix and chill"?
«Netflix and chill» — это популярный интернет-сленг, который изначально означал просто непринужденный просмотр фильмов или сериалов на стриминговой платформе Netflix. Однако со временем фраза приобрела дополнительный, более интимный подтекст и стала использоваться как эвфемизм для приглашения на свидание с намерением провести вместе время в более близком, часто романтическом или сексуальном контексте. Таким образом, когда кто-то предлагает «Netflix and chill», это может означать не только желание посмотреть что-то вместе, но и намерение провести время в более личной обстановке. Фраза стала частью современной поп-культуры и часто используется в шутливом или двусмысленном смысле.
793a3cde8a7ebb9bcf62b533Запрос: Создай пары вопросов-ответов, описывающих термины статьи. Вопрос должен начинаться с "Что такое". В ответе должно быть описание детальное термина из вопроса. Примеры вопросов: "Что такое план счетов?", "Что такое конфигуратор?", "Что такое справочник?". В ответах используй только информацию из статьи. Вопросы и ответы должны иметь законченный и понятный смысл. Вопросов и ответов должно быть немного, они должны быть максимально детальными и развернутыми, не делай кратких вопросов-ответов. Текст должен быть самодостаточным. Не используй выражение "в данном контексте". Включай в ответ символы перевода строки там, где это требуется по смыслу. Включай упоминание "1С:Бухгалтерия 8" (ред. 3.0) в каждый вопрос и ответ там, где это уместно. Формат ответа на запрос должен быть в виде текста MarkDown: [Вопрос] Текст вопроса? [/Вопрос] [Ответ] Текст ответа [/Ответ] ------------------------------ [Вопрос] Текст вопроса? [/Вопрос] [Ответ] Текст ответа [/Ответ] ------------------------------ Текст статьи: Где указать способ отражения расходов на оплату труда для организации в "1С:Бухгалтерии 8" (ред. 3.0)? ?Способ отражения расходов на оплату труда для организации указывается в настройке учета зарплаты: 1. Раздел Зарплата и кадры - Настройки зарплаты - ссылка "Порядок учета зарплаты" . 2. На закладке "Зарплата" в разделе "Отражение в учете зарплаты" в поле "Способ отражения в бух.учете" указывается способ отражения начислений в учете для организации в целом. Значение выбирается из справочника "Способы учета зарплаты" (раздел Зарплата и кадры - Настройки зарплаты - раздел "Отражение в учете" - ссылка "Способы учета зарплаты"). Предопределенным способом отражения расходов на оплату труда в программе является "Отражение начислений по умолчанию". Этот способ настроен в программе таким образом, чтобы по умолчанию отражать начисления проводкой по дебету счета 26 "Общехозяйственные расходы" и кредиту счета 70 "Расчеты с персоналом по оплате труда". Если необходимый способ отражения для организации в справочнике "Способы учета зарплаты" отсутствует, то его необходимо создать. Для каждого способа указывается только счет дебета (счет учета расходов по заработной плате) и аналитика бухгалтерской проводки по начислению сотрудникам заработной платы. Корреспондирующим счетом этой проводки всегда является счет 70. 3. В поле "Действует с" укажите период начиная с которого будет действовать указанный способ отражения. 4. Историю изменений отражения зарплаты в бухгалтерском учете можно посмотреть, нажав на ссылку "История". 5. Способ отражения указанный для организации, будет формироваться по умолчанию в тех случаях, когда виду начисления или для конкретного сотрудника не назначен свой способ отражения.
```markdown [Вопрос] Что такое способ отражения расходов на оплату труда в "1С:Бухгалтерия 8" (ред. 3.0)? [/Вопрос] [Ответ] В "1С:Бухгалтерия 8" (ред. 3.0) способ отражения расходов на оплату труда для организации является частью настройки учета зарплаты, которая устанавливает правила, по которым начисления заработной платы отражаются в бухгалтерском учете. Это важный аспект для обеспечения правильного учета трудозатрат и контроля за расходами на оплату труда. Способ отражения расходов на оплату труда задается в следующем месте: 1. Раздел "Зарплата и кадры" — "Настройки зарплаты", где можно выбрать ссылку "Порядок учета зарплаты". 2. На закладке "Зарплата" в разделе "Отражение в учете зарплаты" указывается конкретный способ отражения в поле "Способ отражения в бух. учете". Этот способ выбирается из справочника "Способы учета зарплаты". Этот справочник можно найти в разделе "Зарплата и кадры" — "Настройки зарплаты" — "Отражение в учете" — "Способы учета зарплаты". Предопределенным способом отражения является "Отражение начислений по умолчанию". По умолчанию он настроен на проведение операций проводкой по дебету счета 26 "Общехозяйственные расходы" и кредиту счета 70 "Расчеты с персоналом по оплате труда". Если требуемый способ отсутствует в системе, его необходимо создать. При создании указывается только счет дебета и аналитика для бухгалтерской проводки по начислению заработной платы с корреспонденцией на счет 70. Для выбранного способа можно установить дату начала его действия в поле "Действует с", а для анализа изменений — воспользоваться опцией "История". Данный способ формируется по умолчанию, если не назначен иной способ для вида начисления или конкретного сотрудника. [/Ответ] ------------------------------ ```