Here is my python code, the layout in the Analysis tab is currently not very user friendly. Could you reorgonize its design and the placement of its elements? In the future, It will implement 3 different methods including Sugihara's CCM, whose control options will be placed there, their results will be shown in the table and a plot will be generated, those will be dynamicaly updated depending on which of these methods was used etc. "import sys
from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
                               QHBoxLayout, QTableWidget, QTableWidgetItem, QLabel, QSpinBox,
                               QPushButton, QTabWidget, QGroupBox, QMenuBar, QMenu, QFileDialog,
                               QMessageBox, QDockWidget, QSplitter, QComboBox, QFrame, QStackedWidget,
                               QLineEdit, QFormLayout, QListWidget, QListWidgetItem, QCheckBox, QHeaderView,
                               QToolBar, QStatusBar, QStyle, QInputDialog)
from PySide6.QtCore import Qt, QPropertyAnimation, QEasingCurve, Signal, QObject, QSize
from PySide6.QtGui import QFont, QColor, QPalette
import pandas as pd
import numpy as np


class ModernStyle:
    """Class to handle application-wide styling"""

    PRIMARY_COLOR = "#2196F3"
    SECONDARY_COLOR = "#FFC107"
    BACKGROUND_COLOR = "#FFFFFF"
    ACCENT_COLOR = "#E91E63"

    @staticmethod
    def get_stylesheet():
        return """
        QMainWindow {
            background-color: #F5F5F5;
        }

        QTableWidget {
            background-color: white;
            alternate-background-color: #F5F5F5;
            selection-background-color: #2196F3;
            selection-color: white;
            border: 1px solid #E0E0E0;
            border-radius: 4px;
        }

        QTableWidget::item {
            padding: 5px;
        }

        QPushButton {
            background-color: #2196F3;
            color: white;
            border: none;
            padding: 8px 15px;
            border-radius: 4px;
            min-width: 80px;
        }

        QPushButton:hover {
            background-color: #1976D2;
        }

        QPushButton:pressed {
            background-color: #0D47A1;
        }

        QGroupBox {
            font-weight: bold;
            border: 1px solid #E0E0E0;
            border-radius: 4px;
            margin-top: 1ex;
            padding: 10px;
            background-color: white;
        }

        QGroupBox::title {
            subcontrol-origin: margin;
            left: 10px;
            padding: 0 3px;
        }

        QComboBox {
            padding: 5px;
            border: 1px solid #E0E0E0;
            border-radius: 4px;
            min-width: 100px;
        }

        QLineEdit {
            padding: 5px;
            border: 1px solid #E0E0E0;
            border-radius: 4px;
        }

        QTabWidget::pane {
            border: 1px solid #E0E0E0;
            border-radius: 4px;
        }

        QTabBar::tab {
            background-color: #F5F5F5;
            padding: 8px 15px;
            margin-right: 2px;
        }

        QTabBar::tab:selected {
            background-color: #2196F3;
            color: white;
        }
        """

    @staticmethod
    def get_compact_stylesheet():
        return """
        QPushButton {
            padding: 3px 8px;
            min-width: 60px;
            max-height: 24px;
        }

        QGroupBox {
            padding: 5px;
            margin-top: 0.5ex;
        }

        QComboBox {
            padding: 3px;
            min-width: 80px;
            max-height: 24px;
        }

        QLineEdit {
            padding: 3px;
            max-height: 24px;
        }
        """


class VariableViewer(QWidget):
    variableChanged = Signal(int, str, str)

    def __init__(self, data_table):
        super().__init__()
        self.data_table = data_table
        self.setup_ui()

    def setup_ui(self):
        layout = QVBoxLayout(self)
        layout.setSpacing(5)  # Reduce spacing
        layout.setContentsMargins(5, 5, 5, 5)

        # Variable table group
        variable_table_group = QGroupBox("Variables")
        variable_table_layout = QVBoxLayout()
        variable_table_layout.setSpacing(3)
        variable_table_layout.setContentsMargins(5, 5, 5, 5)

        self.variable_table = QTableWidget()
        self.variable_table.setColumnCount(2)
        self.variable_table.setHorizontalHeaderLabels(['Variable Name', 'Data Type'])
        self.variable_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.variable_table.setEditTriggers(QTableWidget.NoEditTriggers)  # Prevent direct editing

        variable_table_layout.addWidget(self.variable_table)
        variable_table_group.setLayout(variable_table_layout)
        layout.addWidget(variable_table_group)

        # Connect signals
        self.variable_table.cellDoubleClicked.connect(self.edit_variable_name)
        self.variable_table.currentCellChanged.connect(self.update_variable_details)

        # Add some spacing and margins
        layout.setContentsMargins(10, 10, 10, 10)
        layout.setSpacing(10)

        # Style the table
        self.variable_table.setAlternatingRowColors(True)
        self.variable_table.setStyleSheet("""
            QTableWidget {
                border: 1px solid #E0E0E0;
                border-radius: 4px;
            }
        """)

    def load_variables(self):
        self.variable_table.setRowCount(0)
        for column in range(self.data_table.columnCount()):
            row_position = self.variable_table.rowCount()
            self.variable_table.insertRow(row_position)

            header_item = self.data_table.horizontalHeaderItem(column)
            var_name = header_item.text() if header_item else f"Variable {column}"

            # Variable Name
            name_item = QTableWidgetItem(var_name)
            self.variable_table.setItem(row_position, 0, name_item)

            # Data Type with ComboBox
            type_combo = QComboBox()
            current_type = self.detect_column_type(column)
            type_combo.addItems(["Numeric", "String", "Date", "Boolean"])
            if current_type in ["Numeric", "String", "Date", "Boolean"]:
                type_combo.setCurrentText(current_type)
            else:
                type_combo.setCurrentText("String")
            type_combo.currentTextChanged.connect(lambda new_type, row=row_position: self.on_type_changed(row, new_type))
            self.variable_table.setCellWidget(row_position, 1, type_combo)

    def detect_column_type(self, column):
        # Simple type detection based on pandas data types
        try:
            # Access the data as pandas Series
            column_data = []
            for i in range(self.data_table.rowCount()):
                item = self.data_table.item(i, column)
                if item is not None:
                    column_data.append(item.text())
            series = pd.Series(column_data)
            if pd.api.types.is_numeric_dtype(pd.to_numeric(series, errors='coerce')):
                return "Numeric"
            else:
                return "String"
        except:
            return "String"

    def update_variable_details(self, currentRow, currentColumn, previousRow, previousColumn):
        pass  # Placeholder for future enhancements

    def edit_variable_name(self, row, column):
        if column == 0:  # Only allow editing of Variable Name
            current_name = self.variable_table.item(row, column).text()
            new_name, ok = QInputDialog.getText(self, "Edit Variable Name", "New Name:", text=current_name)
            if ok and new_name:
                self.variable_table.item(row, column).setText(new_name)
                self.data_table.setHorizontalHeaderItem(row, QTableWidgetItem(new_name))
                self.variableChanged.emit(row, new_name, self.get_variable_type(row))

    def on_type_changed(self, row, new_type):
        variable_name = self.variable_table.item(row, 0).text()
        old_type = self.get_variable_type(row)
        if old_type != new_type:
            success, message = self.convert_column_type(row, new_type)
            if success:
                self.variableChanged.emit(row, variable_name, new_type)
                self.parent().statusBar().showMessage(f"Variable '{variable_name}' converted to {new_type}", 2000)
            else:
                # Revert the ComboBox to the old type
                combo = self.variable_table.cellWidget(row, 1)
                combo.blockSignals(True)
                combo.setCurrentText(old_type)
                combo.blockSignals(False)
                QMessageBox.critical(self, "Conversion Error", f"Failed to convert '{variable_name}' to {new_type}.\nError: {message}")

    def get_variable_type(self, row):
        combo = self.variable_table.cellWidget(row, 1)
        return combo.currentText() if combo else "String"

    def convert_column_type(self, row, new_type):
        column = row  # Assuming each row in VariableViewer corresponds to a column in data_table
        try:
            column_data = []
            for i in range(self.data_table.rowCount()):
                item = self.data_table.item(i, column)
                column_data.append(item.text() if item else "")

            if new_type == "Numeric":
                converted_data = pd.to_numeric(column_data, errors='coerce')
                if converted_data.isnull().any():
                    raise ValueError("Some values could not be converted to Numeric.")
            elif new_type == "String":
                converted_data = [str(item) for item in column_data]
            elif new_type == "Date":
                converted_data = pd.to_datetime(column_data, errors='coerce')
                if converted_data.isnull().all():
                    raise ValueError("All values could not be converted to Date.")
                converted_data = converted_data.dt.strftime('%Y-%m-%d %H:%M:%S').fillna('')
            elif new_type == "Boolean":
                # Simple conversion: non-empty strings to True, empty to False
                converted_data = [bool(item) for item in column_data]
            else:
                return False, "Unsupported data type."

            # Update the table with converted data
            for i, value in enumerate(converted_data):
                self.data_table.setItem(i, column, QTableWidgetItem(str(value)))

            return True, ""
        except Exception as e:
            return False, str(e)


class TimeSeriesApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Time Series Analysis Tool")
        self.resize(1200, 800)

        # Set the application style
        self.setStyleSheet(ModernStyle.get_stylesheet() + ModernStyle.get_compact_stylesheet())

        self.data = pd.DataFrame()
        self.setup_ui()

    def setup_ui(self):
        self.create_menu_bar()

        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)

        tab_widget = QTabWidget()
        main_layout.addWidget(tab_widget)

        # Data View Tab
        data_tab = self.setup_data_tab()
        tab_widget.addTab(data_tab, "Data Editor")

        # Analysis Tab
        analysis_tab = self.setup_analysis_tab()
        tab_widget.addTab(analysis_tab, "Analysis")

        # Add a toolbar
        toolbar = self.addToolBar("Main Toolbar")
        toolbar.setMovable(False)

        # Add toolbar actions with icons
        new_action = toolbar.addAction("New")
        new_action.setIcon(self.style().standardIcon(QStyle.SP_FileIcon))
        new_action.triggered.connect(self.new_file)

        open_action = toolbar.addAction("Open")
        open_action.setIcon(self.style().standardIcon(QStyle.SP_DialogOpenButton))
        open_action.triggered.connect(self.open_file)

        save_action = toolbar.addAction("Save")
        save_action.setIcon(self.style().standardIcon(QStyle.SP_DialogSaveButton))
        save_action.triggered.connect(self.save_file)

        # Add status bar
        self.statusBar().showMessage("Ready")

        # Enhance the tab widget
        tab_widget.setDocumentMode(True)
        tab_widget.setTabPosition(QTabWidget.North)

        # Add icons to tabs
        tab_widget.setTabIcon(0, self.style().standardIcon(QStyle.SP_FileDialogDetailedView))
        tab_widget.setTabIcon(1, self.style().standardIcon(QStyle.SP_CommandLink))

    def create_menu_bar(self):
        menubar = self.menuBar()

        file_menu = menubar.addMenu("File")
        new_action = file_menu.addAction("New")
        new_action.triggered.connect(self.new_file)
        open_action = file_menu.addAction("Open")
        open_action.triggered.connect(self.open_file)
        save_action = file_menu.addAction("Save")
        save_action.triggered.connect(self.save_file)
        file_menu.addSeparator()
        exit_action = file_menu.addAction("Exit")
        exit_action.triggered.connect(self.close)

        edit_menu = menubar.addMenu("Edit")
        edit_menu.addAction("Copy")
        edit_menu.addAction("Paste")
        edit_menu.addAction("Clear All")

        help_menu = menubar.addMenu("Help")
        help_menu.addAction("Documentation")
        help_menu.addAction("About")

    def setup_data_tab(self):
        data_tab = QWidget()
        data_layout = QVBoxLayout(data_tab)
        data_layout.setSpacing(5)
        data_layout.setContentsMargins(5, 5, 5, 5)

        # Toolbar for data manipulation
        data_toolbar = QToolBar()
        data_toolbar.setIconSize(QSize(16, 16))  # Smaller icons

        add_row_btn = QPushButton()
        add_row_btn.setIcon(self.style().standardIcon(QStyle.SP_FileDialogNewFolder))
        add_row_btn.setToolTip("Add new row")
        add_row_btn.setMaximumWidth(30)
        add_row_btn.clicked.connect(self.add_row)

        delete_row_btn = QPushButton()
        delete_row_btn.setIcon(self.style().standardIcon(QStyle.SP_DialogCloseButton))
        delete_row_btn.setToolTip("Delete selected row")
        delete_row_btn.setMaximumWidth(30)
        delete_row_btn.clicked.connect(self.delete_row)

        add_column_btn = QPushButton()
        add_column_btn.setIcon(self.style().standardIcon(QStyle.SP_FileDialogNewFolder))
        add_column_btn.setToolTip("Add new column")
        add_column_btn.setMaximumWidth(30)
        add_column_btn.clicked.connect(self.add_column)

        delete_column_btn = QPushButton()
        delete_column_btn.setIcon(self.style().standardIcon(QStyle.SP_DialogCloseButton))
        delete_column_btn.setToolTip("Delete selected column")
        delete_column_btn.setMaximumWidth(30)
        delete_column_btn.clicked.connect(self.delete_column)

        # Add buttons to the toolbar
        data_toolbar.addWidget(add_row_btn)
        data_toolbar.addWidget(delete_row_btn)
        data_toolbar.addSeparator()
        data_toolbar.addWidget(add_column_btn)
        data_toolbar.addWidget(delete_column_btn)

        data_layout.addWidget(data_toolbar)

        # Main splitter with adjusted sizes
        self.data_table = QTableWidget()
        self.data_table.setColumnCount(3)
        self.data_table.setHorizontalHeaderLabels(['Time', 'Variable 1', 'Variable 2'])
        self.data_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)

        self.variable_viewer = VariableViewer(self.data_table)
        self.variable_viewer.variableChanged.connect(self.update_variable)

        splitter = QSplitter(Qt.Horizontal)
        splitter.addWidget(self.data_table)
        splitter.addWidget(self.variable_viewer)
        splitter.setStretchFactor(0, 2)  # Data table gets more space
        splitter.setStretchFactor(1, 1)  # Variable viewer gets less space
        data_layout.addWidget(splitter)

        return data_tab

    def setup_analysis_tab(self):
        analysis_tab = QWidget()
        analysis_layout = QHBoxLayout(analysis_tab)
        analysis_layout.setSpacing(5)
        analysis_layout.setContentsMargins(5, 5, 5, 5)

        # Left side: Plot and controls
        left_panel = QVBoxLayout()

        # Method selection in a compact group
        method_group = QGroupBox("Analysis Method")
        method_layout = QVBoxLayout()
        method_layout.setSpacing(3)
        self.method_combo = QComboBox()
        self.method_combo.setMaximumWidth(200)
        self.method_combo.addItems(
            ["Optimal Time Delay Analysis", "Optimal Embedding Dimension", "Convergent Cross Mapping"])
        self.method_combo.currentIndexChanged.connect(self.update_method_parameters)
        method_layout.addWidget(self.method_combo)
        method_group.setLayout(method_layout)
        left_panel.addWidget(method_group)

        # Plot area
        self.plot_label = QLabel("Plot will appear here")
        self.plot_label.setStyleSheet("background-color: #f0f0f0; border: 1px solid #ccc;")
        self.plot_label.setMinimumSize(400, 300)
        self.plot_label.setAlignment(Qt.AlignCenter)
        left_panel.addWidget(self.plot_label)

        analysis_layout.addLayout(left_panel)

        # Right side: Parameters and results
        right_panel = QVBoxLayout()

        # Parameters in a scrollable area
        params_group = QGroupBox("Parameters")
        self.params_frame = QFrame()
        self.params_frame.setFrameShape(QFrame.StyledPanel)
        self.params_frame.setMaximumWidth(250)
        self.params_layout = QVBoxLayout(self.params_frame)
        params_layout = QVBoxLayout()
        params_layout.addWidget(self.params_frame)
        params_group.setLayout(params_layout)
        right_panel.addWidget(params_group)

        # Results table
        results_group = QGroupBox("Results")
        results_layout = QVBoxLayout()
        self.results_table = QTableWidget()
        self.results_table.setMaximumHeight(200)
        results_layout.addWidget(self.results_table)
        results_group.setLayout(results_layout)
        right_panel.addWidget(results_group)

        analysis_layout.addLayout(right_panel)

        return analysis_tab

    def update_method_parameters(self):
        for i in reversed(range(self.params_layout.count())):
            widget = self.params_layout.itemAt(i).widget()
            if widget:
                widget.setParent(None)

        method = self.method_combo.currentText()
        if method == "Optimal Time Delay Analysis":
            self.add_parameter("Parameter 1", 0, 100)
            self.add_parameter("Parameter 2", 0, 100)
        elif method == "Optimal Embedding Dimension":
            self.add_parameter("Parameter A", 0, 100)
            self.add_parameter("Parameter B", 0, 100)
        elif method == "Convergent Cross Mapping":
            self.add_parameter("Parameter X", 0, 100)
            self.add_parameter("Parameter Y", 0, 100)

    def add_parameter(self, name, min_value, max_value):
        layout = QHBoxLayout()
        layout.addWidget(QLabel(name))
        spinbox = QSpinBox()
        spinbox.setRange(min_value, max_value)
        layout.addWidget(spinbox)
        self.params_layout.addLayout(layout)

    def update_variable(self, row, new_name, new_type):
        # Additional handling if needed when a variable is updated
        self.statusBar().showMessage(f"Variable '{new_name}' updated to type '{new_type}'", 2000)

    def add_row(self):
        current_rows = self.data_table.rowCount()
        self.data_table.insertRow(current_rows)
        self.variable_viewer.load_variables()
        self.statusBar().showMessage("Row added", 2000)

    def delete_row(self):
        current_row = self.data_table.currentRow()
        if current_row >= 0:
            reply = QMessageBox.question(
                self,
                'Delete Row',
                f"Are you sure you want to delete row {current_row + 1}?",
                QMessageBox.Yes | QMessageBox.No,
                QMessageBox.No
            )
            if reply == QMessageBox.Yes:
                self.data_table.removeRow(current_row)
                self.variable_viewer.load_variables()
                self.statusBar().showMessage("Row deleted", 2000)
        else:
            QMessageBox.warning(self, "No Selection", "No row selected to delete.")

    def add_column(self):
        current_cols = self.data_table.columnCount()
        self.data_table.setColumnCount(current_cols + 1)
        self.data_table.setHorizontalHeaderItem(
            current_cols,
            QTableWidgetItem(f"Variable {current_cols}")
        )
        self.variable_viewer.load_variables()
        self.statusBar().showMessage("Column added", 2000)

    def delete_column(self):
        current_column = self.data_table.currentColumn()
        if current_column >= 0:
            reply = QMessageBox.question(
                self,
                'Delete Column',
                f"Are you sure you want to delete column '{self.data_table.horizontalHeaderItem(current_column).text()}'?",
                QMessageBox.Yes | QMessageBox.No,
                QMessageBox.No
            )
            if reply == QMessageBox.Yes:
                self.data_table.removeColumn(current_column)
                self.variable_viewer.load_variables()
                self.statusBar().showMessage("Column deleted", 2000)
        else:
            QMessageBox.warning(self, "No Selection", "No column selected to delete.")

    def new_file(self):
        reply = QMessageBox.question(
            self,
            'New File',
            "Are you sure you want to create a new file? Unsaved changes will be lost.",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )
        if reply == QMessageBox.Yes:
            self.data_table.clearContents()
            self.data_table.setRowCount(0)
            self.data_table.setColumnCount(0)
            self.variable_viewer.load_variables()
            self.statusBar().showMessage("New file created", 2000)

    def open_file(self):
        filename, _ = QFileDialog.getOpenFileName(
            self,
            "Open Data File",
            "",
            "CSV Files (*.csv);;All Files (*)"
        )
        if filename:
            try:
                data = pd.read_csv(filename)
                self.load_data_to_table(data)
                self.variable_viewer.load_variables()
                self.statusBar().showMessage(f"Loaded '{filename}'", 2000)
            except Exception as e:
                QMessageBox.critical(self, "Error", f"Could not open file: {str(e)}")

    def save_file(self):
        filename, _ = QFileDialog.getSaveFileName(
            self,
            "Save Data File",
            "",
            "CSV Files (*.csv);;All Files (*)"
        )
        if filename:
            try:
                data = self.get_data_from_table()
                data.to_csv(filename, index=False)
                self.statusBar().showMessage(f"Saved to '{filename}'", 2000)
            except Exception as e:
                QMessageBox.critical(self, "Error", f"Could not save file: {str(e)}")

    def load_data_to_table(self, data):
        self.data_table.setRowCount(len(data))
        self.data_table.setColumnCount(len(data.columns))
        self.data_table.setHorizontalHeaderLabels(data.columns)

        for i in range(len(data)):
            for j in range(len(data.columns)):
                self.data_table.setItem(
                    i, j,
                    QTableWidgetItem(str(data.iloc[i, j]))
                )

        self.variable_viewer.load_variables()

    def get_data_from_table(self):
        rows = self.data_table.rowCount()
        cols = self.data_table.columnCount()
        data = []

        headers = []
        for j in range(cols):
            header = self.data_table.horizontalHeaderItem(j)
            headers.append(header.text() if header else f"Column {j}")

        for i in range(rows):
            row_data = []
            for j in range(cols):
                item = self.data_table.item(i, j)
                row_data.append(item.text() if item else "")
            data.append(row_data)

        return pd.DataFrame(data, columns=headers)

    def placeholder_action(self, action_name):
        self.results_label.setText(f"{action_name} placeholder - To be implemented")


def main():
    app = QApplication(sys.argv)

    # Set application-wide font
    font = QFont("Segoe UI", 9)
    app.setFont(font)

    # Enable high DPI scaling
    app.setAttribute(Qt.AA_EnableHighDpiScaling, True)
    app.setAttribute(Qt.AA_UseHighDpiPixmaps, True)

    window = TimeSeriesApp()
    window.show()
    sys.exit(app.exec())


if __name__ == "__main__":
    main()"