Mobil Menü

Python ile Profesyonel Resim Editörü: Gelişmiş Paint Uygulaması

Günümüzde dijital sanat ve grafik tasarım dünyasında profesyonel araçlara olan ihtiyaç her geçen gün artıyor. Python programlama dili ve PyQt5 kütüphanesi kullanarak geliştirdiğimiz bu gelişmiş resim editörü, temel paint uygulamalarının çok ötesine geçen özellikler sunuyor.

Neden Bu Paint Uygulamasını Kullanmalısınız?

Bu Python tabanlı resim editörü, hem yeni başlayanlar hem de profesyonel kullanıcılar için tasarlanmış kapsamlı bir çözüm sunar:

  1. Tamamen ücretsiz ve açık kaynak kodlu
  2. Hafif ve hızlı çalışan bir arayüz
  3. Photoshop benzeri katman desteği
  4. Temel ve ileri düzey çizim araçları
  5. Animasyon oluşturma özellikleri

Temel Özellikler

1. Kapsamlı Çizim Araçları

Uygulamamız standart kalem ve fırça araçlarının yanı sıra:

  • Özel fırça yükleme desteği
  • Çeşitli şekil çizim araçları (dikdörtgen, elips, çokgen, yıldız)
  • Gradyan dolgu seçenekleri
  • Metin ekleme özelliği
  • Simetri modu ile sanatsal çizimler

2. Profesyonel Katman Yönetimi

Photoshop benzeri katman özellikleri sayesinde:

  • Sınırsız sayıda katman oluşturabilir
  • Katman görünürlüğünü kontrol edebilir
  • Katmanları sürükle-bırak ile yeniden sıralayabilir
  • Katmanları birleştirebilirsiniz

3. Animasyon Desteği

Basit animasyonlar oluşturmak için:

  • Çoklu kare (frame) desteği
  • Animasyon önizleme ve oynatma
  • GIF ve APNG formatlarında kaydetme
  • Kare hızını ayarlayabilme

Teknik Detaylar

Bu Python paint uygulaması, PyQt5’in güçlü grafik yetenekleri üzerine inşa edilmiştir. Temel teknolojiler:

  • PyQt5 için QImage ve QPainter sınıfları
  • Pillow kütüphanesi ile gelişmiş filtreler
  • NumPy ile piksel işlemleri
  • JSON tabanlı proje kaydetme/yükleme

Kullanım Senaryoları

Bu gelişmiş resim editörü ile:

  1. Dijital sanat çalışmaları oluşturabilir
  2. Web tasarımı için basit grafikler hazırlayabilir
  3. Eğitim amaçlı çizimler yapabilir
  4. Basit animasyonlar oluşturabilir
  5. Fotoğraf düzenleme işlemleri gerçekleştirebilirsiniz

Kurulum ve Kullanım

Uygulamayı bilgisayarınıza kurmak için:

  1. Gerekli kütüphaneleri yükleyin:
   pip install PyQt5 numpy pillow
  1. Kodu bir Python dosyasına kaydedin (örneğin super_paint.py)
  2. Uygulamayı çalıştırın:
   python super_paint.py

Sonuç

Bu Python ile geliştirilmiş resim editörü, profesyonel grafik yazılımlarına alternatif olarak geliştirilmiş güçlü bir araçtır. Açık kaynak kodlu yapısı sayesinde geliştiriciler tarafından özelleştirilebilir ve genişletilebilir.

Ücretsiz, hafif ve kullanıcı dostu arayüzüyle, dijital içerik oluşturucular için mükemmel bir seçenek sunar. Temel çizim ihtiyaçlarınızdan profesyonel grafik çalışmalarına kadar geniş bir yelpazede kullanılabilir.

Python ile neler yapılabileceğinin bir göstergesi olan bu uygulama, programlama ve dijital sanatı bir araya getiren harika bir örnektir.

import sys
import os
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QLabel, QToolBar, QAction,
                             QColorDialog, QFileDialog, QSpinBox, QComboBox, QFontDialog,
                             QInputDialog, QMessageBox, QDockWidget, QListWidget, QVBoxLayout,
                             QHBoxLayout, QPushButton, QSlider, QCheckBox, QGroupBox, QListWidgetItem, QTabWidget)
from PyQt5.QtGui import (QPainter, QPen, QColor, QImage, QPixmap, QFont, QIcon,
                         QPainterPath, QTextCursor, QTextDocument, QCursor, QLinearGradient,
                         QRadialGradient, QConicalGradient, QGradient, QPolygon, QPolygonF, QBrush, QTransform)
from PyQt5.QtCore import Qt, QPoint, QRect, QSize, QEvent, QPointF, QLineF, QRectF, QBuffer, QByteArray
from PyQt5.QtPrintSupport import QPrinter, QPrintDialog
import numpy as np
from PIL import Image, ImageFilter, ImageEnhance, ImageDraw
import json
from datetime import datetime


class Canvas(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.parent = parent
        self.image = QImage(QSize(800, 600), QImage.Format_ARGB32)
        self.image.fill(Qt.white)
        self.drawing = False
        self.last_point = QPoint()
        self.pen_color = QColor(Qt.black)
        self.pen_width = 3
        self.alpha = 255
        self.tool = "pen"
        self.font = QFont("Arial", 12)
        self.text_cursor_pos = None
        self.shapes = []
        self.current_shape = None
        self.undo_stack = []
        self.redo_stack = []
        self.zoom_factor = 1.0
        self.setMouseTracking(True)

        # Drawing tools properties
        self.brush_style = Qt.SolidLine
        self.cap_style = Qt.RoundCap
        self.join_style = Qt.RoundJoin
        self.fill_color = QColor(Qt.white)
        self.gradient_type = "none"
        self.gradient = None
        self.pattern = None
        self.eraser_mode = False

        # Selection tools
        self.selection_start = None
        self.selection_end = None
        self.selected_area = None
        self.is_moving_selection = False
        self.selection_offset = QPoint()
        self.selection_buffer = None

        # Text tool
        self.text_buffer = ""
        self.text_item = None
        self.text_edit_mode = False

        # Shapes
        self.polygon_points = []
        self.star_points = 5
        self.arrow_size = 10

        # Filters and effects
        self.current_filter = None
        self.filter_intensity = 1.0

        # Layers
        self.layers = [self.image.copy()]
        self.current_layer_index = 0
        self.layer_visibility = [True]

        # Eyedropper
        self.eyedropper_color = None

        # Ruler and guides
        self.show_ruler = False
        self.guides = []
        self.grid_size = 20
        self.show_grid = False

        # History for brush strokes
        self.brush_history = []

        # Symmetry
        self.symmetry_mode = False
        self.symmetry_axis = "vertical"
        self.symmetry_count = 2

        # Custom brushes
        self.custom_brush = None
        self.brush_spacing = 10
        self.brush_scatter = 0
        self.brush_rotation = 0

        # Animation
        self.frames = [self.image.copy()]
        self.current_frame = 0
        self.animation_speed = 100  # ms
        self.animation_timer = None
        self.playing_animation = False

        # Perspective tools
        self.perspective_points = []
        self.perspective_mode = False

        # AI tools
        self.ai_model_loaded = False

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            pos = event.pos() / self.zoom_factor
            self.drawing = True
            self.last_point = pos

            if self.tool == "text":
                self.handle_text_tool(pos)
            elif self.tool == "selection":
                self.handle_selection_tool(pos)
            elif self.tool == "eyedropper":
                self.sample_color(event.pos())
            elif self.tool in ["rect", "ellipse", "line", "roundrect", "polygon", "star", "arrow"]:
                self.handle_shape_tools(pos)
            elif self.tool == "fill":
                self.flood_fill(pos)
            elif self.tool == "perspective":
                self.handle_perspective_tool(pos)
            else:
                self.save_undo_state()
                if self.tool == "pen" and self.custom_brush:
                    self.apply_custom_brush(pos)

    def mouseMoveEvent(self, event):
        if (event.buttons() & Qt.LeftButton) and self.drawing:
            pos = event.pos() / self.zoom_factor

            if self.tool in ["pen", "brush", "airbrush", "eraser"]:
                self.handle_drawing_tools(pos)
            elif self.tool == "selection":
                self.update_selection(pos)
            elif self.tool in ["rect", "ellipse", "line", "roundrect", "arrow"] and self.current_shape:
                self.update_shape(pos)
            elif self.tool == "move" and self.selected_area:
                self.move_selection(pos)
            elif self.tool == "polygon":
                self.update_polygon(pos)
            elif self.tool == "star":
                self.update_star(pos)

            self.update()

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton and self.drawing:
            pos = event.pos() / self.zoom_factor
            self.drawing = False

            if self.tool == "selection":
                self.finalize_selection(pos)
            elif self.tool in ["rect", "ellipse", "line", "roundrect", "arrow"] and self.current_shape:
                self.finalize_shape()
            elif self.tool == "polygon":
                self.finalize_polygon(pos)
            elif self.tool == "star":
                self.finalize_star(pos)
            elif self.tool == "perspective":
                self.finalize_perspective(pos)

            self.update()

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing, True)
        painter.setRenderHint(QPainter.SmoothPixmapTransform, True)

        # Apply zoom
        painter.scale(self.zoom_factor, self.zoom_factor)

        # Draw grid if enabled
        if self.show_grid:
            self.draw_grid(painter)

        # Draw guides if any
        self.draw_guides(painter)

        # Draw ruler if enabled
        if self.show_ruler:
            self.draw_ruler(painter)

        # Draw the current layer
        painter.drawImage(QPoint(0, 0), self.layers[self.current_layer_index])

        # Draw current shapes (preview)
        if self.current_shape:
            self.draw_shape(painter, self.current_shape)

        # Draw polygon in progress
        if self.tool == "polygon" and self.polygon_points:
            self.draw_polygon_preview(painter)

        # Draw star in progress
        if self.tool == "star" and self.current_shape:
            self.draw_star_preview(painter)

        # Draw selection rectangle
        if self.tool == "selection" and self.selection_start and self.selection_end:
            self.draw_selection_preview(painter)

        # Draw selected area while moving
        if self.tool == "move" and self.selected_area and self.is_moving_selection:
            self.draw_moving_selection(painter)

        # Draw text cursor if in text mode
        if self.tool == "text" and self.text_cursor_pos:
            self.draw_text_cursor(painter)

        # Draw perspective points
        if self.tool == "perspective" and self.perspective_points:
            self.draw_perspective_points(painter)

    # [Previous tool handling methods...]

    def handle_text_tool(self, pos):
        if self.text_cursor_pos:
            self.finish_text_input()
        self.text_cursor_pos = pos
        self.text_buffer = ""
        self.text_edit_mode = True
        self.update()

    def handle_selection_tool(self, pos):
        self.selection_start = pos
        self.selection_end = pos
        self.selected_area = None
        self.selection_buffer = None

    def handle_shape_tools(self, pos):
        self.current_shape = {
            "tool": self.tool,
            "start": pos,
            "end": pos,
            "color": self.pen_color,
            "fill_color": self.fill_color,
            "width": self.pen_width,
            "alpha": self.alpha,
            "fill": self.parent.fill_tool.isChecked(),
            "gradient": self.gradient_type,
            "brush_style": self.brush_style,
            "cap_style": self.cap_style,
            "join_style": self.join_style
        }

        if self.tool == "arrow":
            self.current_shape["arrow_size"] = self.arrow_size

    def handle_drawing_tools(self, pos):
        painter = QPainter(self.layers[self.current_layer_index])
        painter.setCompositionMode(QPainter.CompositionMode_SourceOver)

        if self.tool == "eraser" or self.eraser_mode:
            painter.setCompositionMode(QPainter.CompositionMode_Clear)
            pen = QPen(Qt.white, self.pen_width, self.brush_style, self.cap_style, self.join_style)
        else:
            pen = QPen(self.pen_color,
                       self.pen_width if self.tool == "pen" else self.pen_width * 3,
                       self.brush_style, self.cap_style, self.join_style)

        if self.gradient_type != "none" and not self.eraser_mode:
            self.setup_gradient(painter)

        painter.setPen(pen)
        painter.drawLine(self.last_point, pos)

        if self.tool == "airbrush":
            self.apply_airbrush_effect(painter, pos)

        self.last_point = pos
        self.update()

    def update_selection(self, pos):
        self.selection_end = pos
        self.update()

    def update_shape(self, pos):
        self.current_shape["end"] = pos
        self.update()

    def move_selection(self, pos):
        self.is_moving_selection = True
        self.selection_offset = pos - self.selection_start
        self.update()

    def update_polygon(self, pos):
        if len(self.polygon_points) < 2:
            return
        self.polygon_points[-1] = pos
        self.update()

    def update_star(self, pos):
        if not self.current_shape:
            return
        self.current_shape["end"] = pos
        self.update()

    def finalize_selection(self, pos):
        self.selection_end = pos
        rect = QRect(self.selection_start, self.selection_end).normalized()
        if rect.width() > 2 and rect.height() > 2:
            self.selected_area = self.layers[self.current_layer_index].copy(rect)
            self.selection_buffer = self.layers[self.current_layer_index].copy(rect)
            self.selection_start = rect.topLeft()
            self.selection_end = rect.bottomRight()
        else:
            self.selected_area = None
            self.selection_buffer = None

    def finalize_shape(self):
        if self.current_shape:
            self.draw_shape_to_image(self.current_shape)
            self.save_undo_state()
            self.current_shape = None

    def finalize_polygon(self, pos):
        if len(self.polygon_points) > 2:
            self.polygon_points.append(pos)
            self.draw_polygon_to_image()
            self.save_undo_state()
        self.polygon_points = []
        self.update()

    def finalize_star(self, pos):
        if self.current_shape:
            self.current_shape["end"] = pos
            self.draw_star_to_image()
            self.save_undo_state()
            self.current_shape = None

    def finalize_perspective(self, pos):
        if len(self.perspective_points) == 4:
            self.apply_perspective_transform()
            self.save_undo_state()
        self.perspective_points = []
        self.update()

    def draw_shape(self, painter, shape):
        pen = QPen(shape["color"], shape["width"], shape["brush_style"],
                   shape["cap_style"], shape["join_style"])
        painter.setPen(pen)

        if shape["fill"]:
            if shape["gradient"] != "none":
                self.setup_gradient(painter, shape)
            else:
                painter.setBrush(shape["fill_color"])
        else:
            painter.setBrush(Qt.NoBrush)

        start = shape["start"]
        end = shape["end"]
        rect = QRect(start, end).normalized()

        if shape["tool"] == "rect":
            painter.drawRect(rect)
        elif shape["tool"] == "ellipse":
            painter.drawEllipse(rect)
        elif shape["tool"] == "line":
            painter.drawLine(start, end)
        elif shape["tool"] == "roundrect":
            painter.drawRoundedRect(rect, 10, 10)
        elif shape["tool"] == "arrow":
            self.draw_arrow(painter, start, end, shape["arrow_size"])
        elif shape["tool"] == "star":
            self.draw_star(painter, start, end, self.star_points)

    def draw_shape_to_image(self, shape):
        painter = QPainter(self.layers[self.current_layer_index])
        painter.setRenderHint(QPainter.Antialiasing)
        self.draw_shape(painter, shape)
        painter.end()
        self.update()

    def draw_polygon_preview(self, painter):
        pen = QPen(self.pen_color, self.pen_width, self.brush_style, self.cap_style, self.join_style)
        painter.setPen(pen)

        if self.parent.fill_tool.isChecked():
            painter.setBrush(self.fill_color)
        else:
            painter.setBrush(Qt.NoBrush)

        if len(self.polygon_points) > 1:
            polygon = QPolygonF(self.polygon_points)
            painter.drawPolygon(polygon)

            # Draw connecting line to mouse position
            if len(self.polygon_points) > 0:
                painter.drawLine(self.polygon_points[-1], self.last_point)

    def draw_polygon_to_image(self):
        if len(self.polygon_points) < 3:
            return

        painter = QPainter(self.layers[self.current_layer_index])
        painter.setRenderHint(QPainter.Antialiasing)

        pen = QPen(self.pen_color, self.pen_width, self.brush_style, self.cap_style, self.join_style)
        painter.setPen(pen)

        if self.parent.fill_tool.isChecked():
            painter.setBrush(self.fill_color)
        else:
            painter.setBrush(Qt.NoBrush)

        polygon = QPolygonF(self.polygon_points)
        painter.drawPolygon(polygon)
        painter.end()
        self.update()

    def draw_star_preview(self, painter):
        if not self.current_shape:
            return

        start = self.current_shape["start"]
        end = self.current_shape["end"]
        self.draw_star(painter, start, end, self.star_points)

    def draw_star_to_image(self):
        if not self.current_shape:
            return

        painter = QPainter(self.layers[self.current_layer_index])
        painter.setRenderHint(QPainter.Antialiasing)
        self.draw_star(painter, self.current_shape["start"], self.current_shape["end"], self.star_points)
        painter.end()
        self.update()

    def draw_star(self, painter, start, end, points):
        pen = QPen(self.current_shape["color"], self.current_shape["width"],
                   self.current_shape["brush_style"], self.current_shape["cap_style"],
                   self.current_shape["join_style"])
        painter.setPen(pen)

        if self.current_shape["fill"]:
            painter.setBrush(self.current_shape["fill_color"])
        else:
            painter.setBrush(Qt.NoBrush)

        center = (start + end) / 2
        radius = QLineF(center, end).length()

        star = QPainterPath()
        angle_step = 2 * np.pi / points
        inner_radius = radius * 0.4

        for i in range(points * 2):
            angle = i * angle_step / 2
            r = inner_radius if i % 2 else radius
            x = center.x() + r * np.cos(angle)
            y = center.y() + r * np.sin(angle)

            if i == 0:
                star.moveTo(x, y)
            else:
                star.lineTo(x, y)

        star.closeSubpath()
        painter.drawPath(star)

    def draw_arrow(self, painter, start, end, size):
        line = QLineF(start, end)
        angle = line.angle()

        # Draw the line
        painter.drawLine(line)

        # Draw arrow head
        arrow_head1 = line.p2() - QPointF(
            size * np.cos(np.radians(angle + 150)),
            size * np.sin(np.radians(angle + 150)))

        arrow_head2 = line.p2() - QPointF(
            size * np.cos(np.radians(angle - 150)),
            size * np.sin(np.radians(angle - 150)))

        arrow = QPolygonF([line.p2(), arrow_head1, arrow_head2])
        painter.drawPolygon(arrow)

    def draw_selection_preview(self, painter):
        rect = QRect(self.selection_start, self.selection_end).normalized()
        painter.setPen(QPen(Qt.blue, 1, Qt.DashLine))
        painter.setBrush(QColor(0, 0, 255, 50))
        painter.drawRect(rect)

    def draw_moving_selection(self, painter):
        if self.selected_area:
            painter.drawImage(self.selection_start + self.selection_offset, self.selected_area)

    def draw_text_cursor(self, painter):
        painter.setPen(QPen(Qt.black, 1, Qt.DashLine))
        painter.drawLine(self.text_cursor_pos.x(), self.text_cursor_pos.y() - 10,
                         self.text_cursor_pos.x(), self.text_cursor_pos.y() + 10)

        if self.text_buffer:
            painter.setPen(QPen(self.pen_color, 1))
            painter.setFont(self.font)
            painter.drawText(self.text_cursor_pos, self.text_buffer)

    def draw_perspective_points(self, painter):
        painter.setPen(QPen(Qt.red, 2))
        painter.setBrush(QBrush(Qt.red))

        for point in self.perspective_points:
            painter.drawEllipse(point, 5, 5)

        if len(self.perspective_points) > 1:
            for i in range(len(self.perspective_points)):
                start = self.perspective_points[i]
                end = self.perspective_points[(i + 1) % len(self.perspective_points)]
                painter.drawLine(start, end)

    def draw_grid(self, painter):
        painter.setPen(QPen(QColor(200, 200, 200, 50), 1))

        width = self.layers[self.current_layer_index].width()
        height = self.layers[self.current_layer_index].height()

        for x in range(0, width, self.grid_size):
            painter.drawLine(x, 0, x, height)

        for y in range(0, height, self.grid_size):
            painter.drawLine(0, y, width, y)

    def draw_guides(self, painter):
        painter.setPen(QPen(Qt.blue, 1, Qt.DashLine))

        for guide in self.guides:
            if guide["orientation"] == "horizontal":
                painter.drawLine(0, guide["position"],
                                 self.layers[self.current_layer_index].width(), guide["position"])
            else:
                painter.drawLine(guide["position"], 0,
                                 guide["position"], self.layers[self.current_layer_index].height())

    def draw_ruler(self, painter):
        width = self.layers[self.current_layer_index].width()
        height = self.layers[self.current_layer_index].height()

        # Draw horizontal ruler
        painter.setPen(QPen(Qt.black, 1))
        painter.drawLine(0, 20, width, 20)

        for x in range(0, width, 50):
            painter.drawLine(x, 20, x, 15)
            painter.drawText(x + 2, 15, str(x))

        # Draw vertical ruler
        painter.drawLine(20, 0, 20, height)

        for y in range(0, height, 50):
            painter.drawLine(20, y, 15, y)
            painter.drawText(5, y + 15, str(y))

    def apply_airbrush_effect(self, painter, pos):
        radius = self.pen_width * 2
        density = self.parent.airbrush_density.value()

        for _ in range(density):
            angle = np.random.uniform(0, 2 * np.pi)
            distance = np.random.uniform(0, radius)
            x = pos.x() + distance * np.cos(angle)
            y = pos.y() + distance * np.sin(angle)

            alpha = int(self.alpha * (1 - distance / radius))
            color = QColor(self.pen_color)
            color.setAlpha(alpha)

            painter.setPen(QPen(color, 1))
            painter.drawPoint(QPointF(x, y))

    def apply_custom_brush(self, pos):
        if not self.custom_brush:
            return

        painter = QPainter(self.layers[self.current_layer_index])
        painter.setRenderHint(QPainter.Antialiasing)
        painter.setRenderHint(QPainter.SmoothPixmapTransform, True)

        spacing = self.brush_spacing
        scatter = self.brush_scatter
        rotation = self.brush_rotation

        if self.brush_history:
            last_pos = self.brush_history[-1]
            distance = QLineF(last_pos, pos).length()

            if distance > spacing:
                steps = int(distance / spacing)
                for i in range(1, steps + 1):
                    step_pos = last_pos + (pos - last_pos) * (i * spacing / distance)

                    # Apply scatter
                    if scatter > 0:
                        offset_x = np.random.uniform(-scatter, scatter)
                        offset_y = np.random.uniform(-scatter, scatter)
                        step_pos += QPointF(offset_x, offset_y)

                    # Apply rotation
                    if rotation != 0:
                        angle = np.random.uniform(-rotation, rotation)
                        transform = QTransform().rotate(angle)
                        rotated_brush = self.custom_brush.transformed(transform)
                    else:
                        rotated_brush = self.custom_brush

                    # Draw the brush
                    brush_rect = QRectF(0, 0, rotated_brush.width(), rotated_brush.height())
                    brush_rect.moveCenter(step_pos)
                    painter.drawImage(brush_rect, rotated_brush, rotated_brush.rect())

        # Always draw at current position
        brush_rect = QRectF(0, 0, self.custom_brush.width(), self.custom_brush.height())
        brush_rect.moveCenter(pos)
        painter.drawImage(brush_rect, self.custom_brush, self.custom_brush.rect())

        self.brush_history.append(pos)
        painter.end()
        self.update()

    def setup_gradient(self, painter, shape=None):
        if not shape:
            shape = self.current_shape

        if shape["gradient"] == "linear":
            gradient = QLinearGradient(shape["start"], shape["end"])
        elif shape["gradient"] == "radial":
            radius = QLineF(shape["start"], shape["end"]).length()
            gradient = QRadialGradient(shape["start"], radius)
        elif shape["gradient"] == "conical":
            angle = QLineF(shape["start"], shape["end"]).angle()
            gradient = QConicalGradient(shape["start"], angle)

        if shape["gradient"] != "none":
            gradient.setColorAt(0, shape["color"])
            gradient.setColorAt(1, shape["fill_color"])
            painter.setBrush(QBrush(gradient))

    def flood_fill(self, pos):
        image = self.layers[self.current_layer_index]
        target_color = QColor(image.pixel(pos))
        fill_color = self.pen_color

        if target_color == fill_color:
            return

        self.save_undo_state()

        # Convert QImage to PIL Image for flood fill
        pil_image = Image.fromqimage(image)
        pil_image = pil_image.convert("RGBA")

        # Perform flood fill
        ImageDraw.floodfill(
            pil_image,
            (int(pos.x()), int(pos.y())),
            (fill_color.red(), fill_color.green(), fill_color.blue(), fill_color.alpha()),
            thresh=10
        )

        # Convert back to QImage
        self.layers[self.current_layer_index] = pil_image.toqimage()
        self.update()

    def apply_filter(self, filter_name):
        self.save_undo_state()

        # Convert QImage to PIL Image
        pil_image = Image.fromqimage(self.layers[self.current_layer_index])

        # Apply selected filter
        if filter_name == "blur":
            pil_image = pil_image.filter(ImageFilter.BLUR)
        elif filter_name == "contour":
            pil_image = pil_image.filter(ImageFilter.CONTOUR)
        elif filter_name == "sharpen":
            pil_image = pil_image.filter(ImageFilter.SHARPEN)
        elif filter_name == "smooth":
            pil_image = pil_image.filter(ImageFilter.SMOOTH)
        elif filter_name == "emboss":
            pil_image = pil_image.filter(ImageFilter.EMBOSS)
        elif filter_name == "edge_enhance":
            pil_image = pil_image.filter(ImageFilter.EDGE_ENHANCE)
        elif filter_name == "find_edges":
            pil_image = pil_image.filter(ImageFilter.FIND_EDGES)
        elif filter_name == "grayscale":
            pil_image = ImageEnhance.Color(pil_image).enhance(0)
        elif filter_name == "sepia":
            sepia_filter = [
                0.393, 0.769, 0.189,
                0.349, 0.686, 0.168,
                0.272, 0.534, 0.131
            ]
            pil_image = pil_image.convert("RGB").filter(ImageFilter.Color3DLUT(sepia_filter))

        # Convert back to QImage
        self.layers[self.current_layer_index] = pil_image.toqimage()
        self.update()

    def apply_perspective_transform(self):
        if len(self.perspective_points) != 4:
            return

        self.save_undo_state()

        # Convert QImage to PIL Image
        pil_image = Image.fromqimage(self.layers[self.current_layer_index])

        # Get source and destination points
        width, height = pil_image.size
        src_points = [(0, 0), (width, 0), (width, height), (0, height)]
        dst_points = [(p.x(), p.y()) for p in self.perspective_points]

        # Calculate perspective transform
        coeffs = self.find_coefficients(src_points, dst_points)
        pil_image = pil_image.transform(
            pil_image.size,
            Image.PERSPECTIVE,
            coeffs,
            Image.BICUBIC
        )

        # Convert back to QImage
        self.layers[self.current_layer_index] = pil_image.toqimage()
        self.update()

    def find_coefficients(self, src, dst):
        A = []
        B = []
        for (x, y), (X, Y) in zip(src, dst):
            A.append([x, y, 1, 0, 0, 0, -X * x, -X * y])
            A.append([0, 0, 0, x, y, 1, -Y * x, -Y * y])
            B.append(X)
            B.append(Y)

        A = np.array(A)
        B = np.array(B)

        # Solve the system of equations
        coeffs = np.linalg.lstsq(A, B, rcond=None)[0]

        # Return as 8-tuple with 1 at the end
        return np.append(coeffs, 1).tolist()

    def add_layer(self):
        new_layer = QImage(self.layers[0].size(), QImage.Format_ARGB32)
        new_layer.fill(Qt.transparent)
        self.layers.append(new_layer)
        self.layer_visibility.append(True)
        self.current_layer_index = len(self.layers) - 1
        self.parent.update_layer_list()
        self.update()

    def remove_layer(self, index):
        if len(self.layers) > 1:
            self.layers.pop(index)
            self.layer_visibility.pop(index)
            if self.current_layer_index >= index:
                self.current_layer_index = max(0, self.current_layer_index - 1)
            self.parent.update_layer_list()
            self.update()

    def merge_layers(self):
        if len(self.layers) > 1:
            merged_image = QImage(self.layers[0].size(), QImage.Format_ARGB32)
            merged_image.fill(Qt.transparent)

            painter = QPainter(merged_image)
            for i, layer in enumerate(self.layers):
                if self.layer_visibility[i]:
                    painter.drawImage(0, 0, layer)
            painter.end()

            self.layers = [merged_image]
            self.layer_visibility = [True]
            self.current_layer_index = 0
            self.parent.update_layer_list()
            self.update()

    def toggle_layer_visibility(self, index):
        if 0 <= index < len(self.layer_visibility):
            self.layer_visibility[index] = not self.layer_visibility[index]
            self.update()

    def move_layer_up(self, index):
        if index < len(self.layers) - 1:
            self.layers[index], self.layers[index + 1] = self.layers[index + 1], self.layers[index]
            self.layer_visibility[index], self.layer_visibility[index + 1] = self.layer_visibility[index + 1], \
            self.layer_visibility[index]
            if self.current_layer_index == index:
                self.current_layer_index += 1
            elif self.current_layer_index == index + 1:
                self.current_layer_index -= 1
            self.parent.update_layer_list()
            self.update()

    def move_layer_down(self, index):
        if index > 0:
            self.layers[index], self.layers[index - 1] = self.layers[index - 1], self.layers[index]
            self.layer_visibility[index], self.layer_visibility[index - 1] = self.layer_visibility[index - 1], \
            self.layer_visibility[index]
            if self.current_layer_index == index:
                self.current_layer_index -= 1
            elif self.current_layer_index == index - 1:
                self.current_layer_index += 1
            self.parent.update_layer_list()
            self.update()

    def add_frame(self):
        new_frame = QImage(self.frames[0].size(), QImage.Format_ARGB32)
        new_frame.fill(Qt.transparent)
        self.frames.append(new_frame)
        self.current_frame = len(self.frames) - 1
        self.parent.update_frame_list()
        self.update()

    def remove_frame(self, index):
        if len(self.frames) > 1:
            self.frames.pop(index)
            if self.current_frame >= index:
                self.current_frame = max(0, self.current_frame - 1)
            self.parent.update_frame_list()
            self.update()

    def play_animation(self):
        if not self.playing_animation:
            self.playing_animation = True
            self.animation_timer = self.startTimer(self.animation_speed)
            self.parent.play_button.setText("Durdur")
        else:
            self.playing_animation = False
            self.killTimer(self.animation_timer)
            self.animation_timer = None
            self.parent.play_button.setText("Oynat")

    def timerEvent(self, event):
        if event.timerId() == self.animation_timer:
            self.current_frame = (self.current_frame + 1) % len(self.frames)
            self.update()
            self.parent.update_frame_list()

    def save_animation(self):
        file_name, _ = QFileDialog.getSaveFileName(self, "Animasyonu Kaydet", "",
                                                   "GIF (*.gif);;APNG (*.png);;Tüm Dosyalar (*)")
        if file_name:
            if file_name.endswith('.gif'):
                self.save_gif(file_name)
            elif file_name.endswith('.png'):
                self.save_apng(file_name)

    def save_gif(self, file_name):
        # Convert frames to PIL Images
        pil_frames = [Image.fromqimage(frame) for frame in self.frames]

        # Save as GIF
        pil_frames[0].save(
            file_name,
            save_all=True,
            append_images=pil_frames[1:],
            duration=self.animation_speed,
            loop=0,
            optimize=True
        )

    def save_apng(self, file_name):
        # Convert frames to PIL Images
        pil_frames = [Image.fromqimage(frame) for frame in self.frames]

        # Save as APNG
        pil_frames[0].save(
            file_name,
            save_all=True,
            append_images=pil_frames[1:],
            duration=self.animation_speed,
            loop=0,
            default_image=False
        )

    def save_project(self):
        file_name, _ = QFileDialog.getSaveFileName(self, "Projeyi Kaydet", "",
                                                   "Paint Projesi (*.paint);;Tüm Dosyalar (*)")
        if file_name:
            project_data = {
                "version": "1.0",
                "created": datetime.now().isoformat(),
                "modified": datetime.now().isoformat(),
                "width": self.layers[0].width(),
                "height": self.layers[0].height(),
                "current_layer": self.current_layer_index,
                "layers": [],
                "frames": []
            }

            # Save layers
            for layer in self.layers:
                buffer = QBuffer()
                buffer.open(QBuffer.ReadWrite)
                layer.save(buffer, "PNG")
                project_data["layers"].append(buffer.data().toBase64().data().decode())

            # Save frames
            for frame in self.frames:
                buffer = QBuffer()
                buffer.open(QBuffer.ReadWrite)
                frame.save(buffer, "PNG")
                project_data["frames"].append(buffer.data().toBase64().data().decode())

            # Save to file
            with open(file_name, 'w') as f:
                json.dump(project_data, f)

    def load_project(self):
        file_name, _ = QFileDialog.getOpenFileName(self, "Proje Aç", "",
                                                   "Paint Projesi (*.paint);;Tüm Dosyalar (*)")
        if file_name:
            with open(file_name, 'r') as f:
                project_data = json.load(f)

            self.layers = []
            self.frames = []
            self.layer_visibility = []

            # Load layers
            for layer_data in project_data["layers"]:
                image = QImage()
                image.loadFromData(QByteArray.fromBase64(layer_data.encode()))
                self.layers.append(image)
                self.layer_visibility.append(True)

            # Load frames
            for frame_data in project_data["frames"]:
                image = QImage()
                image.loadFromData(QByteArray.fromBase64(frame_data.encode()))
                self.frames.append(image)

            self.current_layer_index = project_data.get("current_layer", 0)
            self.current_frame = 0
            self.parent.update_layer_list()
            self.parent.update_frame_list()
            self.update()

    def save_undo_state(self):
        # Limit undo stack size
        if len(self.undo_stack) >= 50:
            self.undo_stack.pop(0)

        # Save current state of all layers
        state = {
            "layers": [layer.copy() for layer in self.layers],
            "current_layer": self.current_layer_index,
            "layer_visibility": self.layer_visibility.copy()
        }

        self.undo_stack.append(state)
        self.redo_stack.clear()
        self.parent.update_undo_redo_buttons()

    def undo(self):
        if self.undo_stack:
            # Save current state to redo stack
            current_state = {
                "layers": [layer.copy() for layer in self.layers],
                "current_layer": self.current_layer_index,
                "layer_visibility": self.layer_visibility.copy()
            }
            self.redo_stack.append(current_state)

            # Restore previous state
            prev_state = self.undo_stack.pop()
            self.layers = [layer.copy() for layer in prev_state["layers"]]
            self.current_layer_index = prev_state["current_layer"]
            self.layer_visibility = prev_state["layer_visibility"].copy()

            self.update()
            self.parent.update_undo_redo_buttons()
            self.parent.update_layer_list()

    def redo(self):
        if self.redo_stack:
            # Save current state to undo stack
            current_state = {
                "layers": [layer.copy() for layer in self.layers],
                "current_layer": self.current_layer_index,
                "layer_visibility": self.layer_visibility.copy()
            }
            self.undo_stack.append(current_state)

            # Restore next state
            next_state = self.redo_stack.pop()
            self.layers = [layer.copy() for layer in next_state["layers"]]
            self.current_layer_index = next_state["current_layer"]
            self.layer_visibility = next_state["layer_visibility"].copy()

            self.update()
            self.parent.update_undo_redo_buttons()
            self.parent.update_layer_list()

    def clear_canvas(self):
        self.save_undo_state()
        for layer in self.layers:
            layer.fill(Qt.transparent)
        self.update()

    def resize_image(self, width, height):
        self.save_undo_state()
        for i in range(len(self.layers)):
            new_image = QImage(QSize(width, height), QImage.Format_ARGB32)
            new_image.fill(Qt.transparent)
            painter = QPainter(new_image)
            painter.drawImage(0, 0, self.layers[i])
            painter.end()
            self.layers[i] = new_image
        self.update()

    def zoom_in(self):
        self.zoom_factor *= 1.2
        self.update()

    def zoom_out(self):
        self.zoom_factor /= 1.2
        self.update()

    def reset_zoom(self):
        self.zoom_factor = 1.0
        self.update()

    def rotate_image(self, degrees):
        self.save_undo_state()
        transform = QTransform().rotate(degrees)
        for i in range(len(self.layers)):
            self.layers[i] = self.layers[i].transformed(transform)
        self.update()

    def flip_horizontal(self):
        self.save_undo_state()
        for i in range(len(self.layers)):
            self.layers[i] = self.layers[i].mirrored(True, False)
        self.update()

    def flip_vertical(self):
        self.save_undo_state()
        for i in range(len(self.layers)):
            self.layers[i] = self.layers[i].mirrored(False, True)
        self.update()

    def sample_color(self, pos):
        if self.layers[self.current_layer_index].rect().contains(pos / self.zoom_factor):
            self.eyedropper_color = QColor(self.layers[self.current_layer_index].pixel(pos / self.zoom_factor))
            if self.eyedropper_color.isValid():
                self.pen_color = self.eyedropper_color
                self.parent.update_color_preview()

    def add_guide(self, orientation, position):
        self.guides.append({
            "orientation": orientation,
            "position": position
        })
        self.update()

    def remove_guide(self, index):
        if 0 <= index < len(self.guides):
            self.guides.pop(index)
            self.update()

    def toggle_grid(self):
        self.show_grid = not self.show_grid
        self.update()

    def toggle_ruler(self):
        self.show_ruler = not self.show_ruler
        self.update()

    def set_grid_size(self, size):
        self.grid_size = size
        if self.show_grid:
            self.update()

    def set_symmetry(self, enabled, axis="vertical", count=2):
        self.symmetry_mode = enabled
        self.symmetry_axis = axis
        self.symmetry_count = count

    def load_custom_brush(self, file_name):
        image = QImage(file_name)
        if not image.isNull():
            self.custom_brush = image
            return True
        return False

    def set_brush_properties(self, spacing, scatter, rotation):
        self.brush_spacing = spacing
        self.brush_scatter = scatter
        self.brush_rotation = rotation

    def load_ai_model(self, model_path):
        # This would be implemented with actual AI model loading code
        self.ai_model_loaded = True
        return True

    def apply_ai_effect(self, effect_name):
        if not self.ai_model_loaded:
            return False

        # This would be implemented with actual AI effect application
        self.save_undo_state()

        # Simulate AI effect
        if effect_name == "style_transfer":
            # Apply a simple color effect as placeholder
            for i in range(len(self.layers)):
                if self.layer_visibility[i]:
                    painter = QPainter(self.layers[i])
                    painter.setCompositionMode(QPainter.CompositionMode_Overlay)
                    painter.fillRect(self.layers[i].rect(), QColor(255, 200, 150, 50))
                    painter.end()

        self.update()
        return True


class PaintApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Süper Gelişmiş Paint Uygulaması")
        self.setGeometry(100, 100, 1200, 800)

        # Recent files listesini başlat
        self.recent_files = []
        self.max_recent_files = 10

        self.canvas = Canvas(self)
        self.setCentralWidget(self.canvas)

        self.create_toolbar()
        self.create_menu()
        self.create_dock_widgets()

        self.status_bar = self.statusBar()
        self.status_bar.showMessage("Hazır")

        # Color preview
        self.color_preview = QLabel()
        self.color_preview.setFixedSize(32, 32)
        self.update_color_preview()
        self.status_bar.addPermanentWidget(self.color_preview)

        # Zoom label
        self.zoom_label = QLabel("100%")
        self.status_bar.addPermanentWidget(self.zoom_label)

        # Position label
        self.position_label = QLabel("0, 0")
        self.status_bar.addPermanentWidget(self.position_label)

        # Connect canvas signals
        self.canvas.mouseMoveEvent = self.track_mouse_position

        # Load settings
        self.load_settings()

    def track_mouse_position(self, event):
        pos = event.pos() / self.canvas.zoom_factor
        self.position_label.setText(f"{int(pos.x())}, {int(pos.y())}")
        QWidget.mouseMoveEvent(self.canvas, event)

    def create_toolbar(self):
        # Main toolbar
        main_toolbar = QToolBar("Ana Araçlar")
        main_toolbar.setIconSize(QSize(24, 24))
        self.addToolBar(main_toolbar)

        # Drawing tools
        drawing_tools = [
            ("select", "Seçim", "selection.png"),
            ("move", "Taşı", "move.png"),
            ("pen", "Kalem", "pen.png"),
            ("brush", "Fırça", "brush.png"),
            ("airbrush", "Havalı Fırça", "airbrush.png"),
            ("eraser", "Silgi", "eraser.png"),
            ("fill", "Dolgu", "fill.png"),
            ("eyedropper", "Renk Seçici", "eyedropper.png"),
            ("text", "Metin", "text.png")
        ]

        self.tool_actions = {}
        for tool, name, icon in drawing_tools:
            action = QAction(QIcon(f"icons/{icon}"), name, self)
            action.triggered.connect(lambda _, t=tool: self.set_tool(t))
            action.setCheckable(True)
            main_toolbar.addAction(action)
            self.tool_actions[tool] = action

        # Default tool
        self.tool_actions["pen"].setChecked(True)

        main_toolbar.addSeparator()

        # Shape tools
        shape_tools = [
            ("line", "Çizgi", "line.png"),
            ("rect", "Dikdörtgen", "rectangle.png"),
            ("roundrect", "Yuvarlak Dikdörtgen", "roundrect.png"),
            ("ellipse", "Elips", "ellipse.png"),
            ("polygon", "Çokgen", "polygon.png"),
            ("star", "Yıldız", "star.png"),
            ("arrow", "Ok", "arrow.png")
        ]

        for tool, name, icon in shape_tools:
            action = QAction(QIcon(f"icons/{icon}"), name, self)
            action.triggered.connect(lambda _, t=tool: self.set_tool(t))
            action.setCheckable(True)
            main_toolbar.addAction(action)
            self.tool_actions[tool] = action

        main_toolbar.addSeparator()

        # Fill tool
        self.fill_tool = QAction(QIcon("icons/fill.png"), "Dolgu", self)
        self.fill_tool.setCheckable(True)
        main_toolbar.addAction(self.fill_tool)

        # Gradient tool
        self.gradient_combo = QComboBox()
        self.gradient_combo.addItems(["Dolgu Yok", "Doğrusal", "Radyal", "Konik"])
        self.gradient_combo.currentIndexChanged.connect(self.set_gradient_type)
        main_toolbar.addWidget(QLabel(" Gradyan:"))
        main_toolbar.addWidget(self.gradient_combo)

        main_toolbar.addSeparator()

        # Color selection
        color_action = QAction(QIcon("icons/color.png"), "Renk Seç", self)
        color_action.triggered.connect(self.choose_pen_color)
        main_toolbar.addAction(color_action)

        fill_color_action = QAction(QIcon("icons/fill_color.png"), "Dolgu Rengi Seç", self)
        fill_color_action.triggered.connect(self.choose_fill_color)
        main_toolbar.addAction(fill_color_action)

        main_toolbar.addSeparator()

        # Line width
        width_label = QLabel(" Kalınlık:")
        main_toolbar.addWidget(width_label)

        self.width_spin = QSpinBox()
        self.width_spin.setRange(1, 100)
        self.width_spin.setValue(self.canvas.pen_width)
        self.width_spin.valueChanged.connect(self.set_pen_width)
        main_toolbar.addWidget(self.width_spin)

        # Alpha (transparency)
        alpha_label = QLabel(" Şeffaflık:")
        main_toolbar.addWidget(alpha_label)

        self.alpha_spin = QSpinBox()
        self.alpha_spin.setRange(0, 255)
        self.alpha_spin.setValue(self.canvas.alpha)
        self.alpha_spin.valueChanged.connect(self.set_alpha)
        main_toolbar.addWidget(self.alpha_spin)

        # Airbrush density
        self.airbrush_density = QSpinBox()
        self.airbrush_density.setRange(1, 100)
        self.airbrush_density.setValue(20)
        main_toolbar.addWidget(QLabel(" Havalı Fırça:"))
        main_toolbar.addWidget(self.airbrush_density)

        main_toolbar.addSeparator()

        # Undo/Redo
        self.undo_action = QAction(QIcon("icons/undo.png"), "Geri Al", self)
        self.undo_action.triggered.connect(self.canvas.undo)
        self.undo_action.setShortcut("Ctrl+Z")
        main_toolbar.addAction(self.undo_action)

        self.redo_action = QAction(QIcon("icons/redo.png"), "Yinele", self)
        self.redo_action.triggered.connect(self.canvas.redo)
        self.redo_action.setShortcut("Ctrl+Y")
        main_toolbar.addAction(self.redo_action)

        main_toolbar.addSeparator()

        # Zoom
        zoom_in_action = QAction(QIcon("icons/zoom_in.png"), "Yakınlaştır", self)
        zoom_in_action.triggered.connect(self.canvas.zoom_in)
        zoom_in_action.setShortcut("Ctrl++")
        main_toolbar.addAction(zoom_in_action)

        zoom_out_action = QAction(QIcon("icons/zoom_out.png"), "Uzaklaştır", self)
        zoom_out_action.triggered.connect(self.canvas.zoom_out)
        zoom_out_action.setShortcut("Ctrl+-")
        main_toolbar.addAction(zoom_out_action)

        zoom_reset_action = QAction(QIcon("icons/zoom_reset.png"), "Normal Boyut", self)
        zoom_reset_action.triggered.connect(self.canvas.reset_zoom)
        zoom_reset_action.setShortcut("Ctrl+0")
        main_toolbar.addAction(zoom_reset_action)

    def create_dock_widgets(self):
        # Layers dock
        layers_dock = QDockWidget("Katmanlar", self)
        layers_dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        layers_widget = QWidget()
        layers_layout = QVBoxLayout()

        self.layer_list = QListWidget()
        self.layer_list.itemClicked.connect(self.select_layer)
        self.update_layer_list()

        add_layer_btn = QPushButton("Katman Ekle")
        add_layer_btn.clicked.connect(self.canvas.add_layer)

        remove_layer_btn = QPushButton("Katman Sil")
        remove_layer_btn.clicked.connect(self.remove_current_layer)

        merge_layers_btn = QPushButton("Katmanları Birleştir")
        merge_layers_btn.clicked.connect(self.canvas.merge_layers)

        move_up_btn = QPushButton("Yukarı Taşı")
        move_up_btn.clicked.connect(self.move_layer_up)

        move_down_btn = QPushButton("Aşağı Taşı")
        move_down_btn.clicked.connect(self.move_layer_down)

        layers_layout.addWidget(self.layer_list)
        layers_layout.addWidget(add_layer_btn)
        layers_layout.addWidget(remove_layer_btn)
        layers_layout.addWidget(merge_layers_btn)
        layers_layout.addWidget(move_up_btn)
        layers_layout.addWidget(move_down_btn)

        layers_widget.setLayout(layers_layout)
        layers_dock.setWidget(layers_widget)
        self.addDockWidget(Qt.RightDockWidgetArea, layers_dock)

        # Animation dock
        animation_dock = QDockWidget("Animasyon", self)
        animation_dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        animation_widget = QWidget()
        animation_layout = QVBoxLayout()

        self.frame_list = QListWidget()
        self.frame_list.itemClicked.connect(self.select_frame)
        self.update_frame_list()

        add_frame_btn = QPushButton("Kare Ekle")
        add_frame_btn.clicked.connect(self.canvas.add_frame)

        remove_frame_btn = QPushButton("Kare Sil")
        remove_frame_btn.clicked.connect(self.remove_current_frame)

        self.play_button = QPushButton("Oynat")
        self.play_button.clicked.connect(self.canvas.play_animation)

        speed_slider = QSlider(Qt.Horizontal)
        speed_slider.setRange(10, 1000)
        speed_slider.setValue(self.canvas.animation_speed)
        speed_slider.valueChanged.connect(self.set_animation_speed)

        save_animation_btn = QPushButton("Animasyonu Kaydet")
        save_animation_btn.clicked.connect(self.canvas.save_animation)

        animation_layout.addWidget(self.frame_list)
        animation_layout.addWidget(add_frame_btn)
        animation_layout.addWidget(remove_frame_btn)
        animation_layout.addWidget(self.play_button)
        animation_layout.addWidget(QLabel("Hız:"))
        animation_layout.addWidget(speed_slider)
        animation_layout.addWidget(save_animation_btn)

        animation_widget.setLayout(animation_layout)
        animation_dock.setWidget(animation_widget)
        self.addDockWidget(Qt.RightDockWidgetArea, animation_dock)

        # Tools dock
        tools_dock = QDockWidget("Araç Özellikleri", self)
        tools_dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        tools_widget = QWidget()
        tools_layout = QVBoxLayout()

        # Brush properties
        brush_group = QGroupBox("Fırça Özellikleri")
        brush_layout = QVBoxLayout()

        brush_style_combo = QComboBox()
        brush_style_combo.addItems(["Düz", "Kesikli", "Noktalı", "Çizgili Noktalı"])
        brush_style_combo.currentIndexChanged.connect(self.set_brush_style)
        brush_layout.addWidget(QLabel("Fırça Stili:"))
        brush_layout.addWidget(brush_style_combo)

        cap_style_combo = QComboBox()
        cap_style_combo.addItems(["Yuvarlak", "Düz", "Kare"])
        cap_style_combo.currentIndexChanged.connect(self.set_cap_style)
        brush_layout.addWidget(QLabel("Uç Stili:"))
        brush_layout.addWidget(cap_style_combo)

        join_style_combo = QComboBox()
        join_style_combo.addItems(["Yuvarlak", "Eğimli", "Kesikli"])
        join_style_combo.currentIndexChanged.connect(self.set_join_style)
        brush_layout.addWidget(QLabel("Birleşim Stili:"))
        brush_layout.addWidget(join_style_combo)

        brush_group.setLayout(brush_layout)
        tools_layout.addWidget(brush_group)

        # Custom brush properties
        custom_brush_group = QGroupBox("Özel Fırça")
        custom_brush_layout = QVBoxLayout()

        load_brush_btn = QPushButton("Fırça Yükle")
        load_brush_btn.clicked.connect(self.load_custom_brush)
        custom_brush_layout.addWidget(load_brush_btn)

        spacing_slider = QSlider(Qt.Horizontal)
        spacing_slider.setRange(1, 100)
        spacing_slider.setValue(self.canvas.brush_spacing)
        spacing_slider.valueChanged.connect(self.set_brush_spacing)
        custom_brush_layout.addWidget(QLabel("Aralık:"))
        custom_brush_layout.addWidget(spacing_slider)

        scatter_slider = QSlider(Qt.Horizontal)
        scatter_slider.setRange(0, 100)
        scatter_slider.setValue(self.canvas.brush_scatter)
        scatter_slider.valueChanged.connect(self.set_brush_scatter)
        custom_brush_layout.addWidget(QLabel("Dağılım:"))
        custom_brush_layout.addWidget(scatter_slider)

        rotation_slider = QSlider(Qt.Horizontal)
        rotation_slider.setRange(0, 360)
        rotation_slider.setValue(self.canvas.brush_rotation)
        rotation_slider.valueChanged.connect(self.set_brush_rotation)
        custom_brush_layout.addWidget(QLabel("Döndürme:"))
        custom_brush_layout.addWidget(rotation_slider)

        custom_brush_group.setLayout(custom_brush_layout)
        tools_layout.addWidget(custom_brush_group)

        # Symmetry
        symmetry_group = QGroupBox("Simetri")
        symmetry_layout = QVBoxLayout()

        symmetry_check = QCheckBox("Simetri Modu")
        symmetry_check.stateChanged.connect(self.toggle_symmetry)
        symmetry_layout.addWidget(symmetry_check)

        symmetry_axis_combo = QComboBox()
        symmetry_axis_combo.addItems(["Dikey", "Yatay", "Nokta", "Çoklu"])
        symmetry_axis_combo.currentIndexChanged.connect(self.set_symmetry_axis)
        symmetry_layout.addWidget(QLabel("Simetri Ekseni:"))
        symmetry_layout.addWidget(symmetry_axis_combo)

        symmetry_count_spin = QSpinBox()
        symmetry_count_spin.setRange(2, 12)
        symmetry_count_spin.setValue(self.canvas.symmetry_count)
        symmetry_count_spin.valueChanged.connect(self.set_symmetry_count)
        symmetry_layout.addWidget(QLabel("Simetri Sayısı:"))
        symmetry_layout.addWidget(symmetry_count_spin)

        symmetry_group.setLayout(symmetry_layout)
        tools_layout.addWidget(symmetry_group)

        tools_widget.setLayout(tools_layout)
        tools_dock.setWidget(tools_widget)
        self.addDockWidget(Qt.LeftDockWidgetArea, tools_dock)

        # Guides and grid dock
        guides_dock = QDockWidget("Kılavuzlar ve Izgara", self)
        guides_dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        guides_widget = QWidget()
        guides_layout = QVBoxLayout()

        # Grid
        grid_group = QGroupBox("Izgara")
        grid_layout = QVBoxLayout()

        grid_check = QCheckBox("Izgarayı Göster")
        grid_check.stateChanged.connect(self.toggle_grid)
        grid_layout.addWidget(grid_check)

        grid_size_spin = QSpinBox()
        grid_size_spin.setRange(5, 100)
        grid_size_spin.setValue(self.canvas.grid_size)
        grid_size_spin.valueChanged.connect(self.set_grid_size)
        grid_layout.addWidget(QLabel("Izgara Boyutu:"))
        grid_layout.addWidget(grid_size_spin)

        snap_check = QCheckBox("Izgaraya Yapış")
        snap_check.stateChanged.connect(self.toggle_snap_to_grid)
        grid_layout.addWidget(snap_check)

        grid_group.setLayout(grid_layout)
        guides_layout.addWidget(grid_group)

        # Ruler
        ruler_group = QGroupBox("Cetvel")
        ruler_layout = QVBoxLayout()

        ruler_check = QCheckBox("Cetveli Göster")
        ruler_check.stateChanged.connect(self.toggle_ruler)
        ruler_layout.addWidget(ruler_check)

        ruler_group.setLayout(ruler_layout)
        guides_layout.addWidget(ruler_group)

        # Guides
        guides_group = QGroupBox("Kılavuzlar")
        guides_list_layout = QVBoxLayout()

        self.guides_list = QListWidget()
        guides_list_layout.addWidget(self.guides_list)

        add_h_guide_btn = QPushButton("Yatay Kılavuz Ekle")
        add_h_guide_btn.clicked.connect(lambda: self.add_guide("horizontal"))
        guides_list_layout.addWidget(add_h_guide_btn)

        add_v_guide_btn = QPushButton("Dikey Kılavuz Ekle")
        add_v_guide_btn.clicked.connect(lambda: self.add_guide("vertical"))
        guides_list_layout.addWidget(add_v_guide_btn)

        remove_guide_btn = QPushButton("Kılavuz Sil")
        remove_guide_btn.clicked.connect(self.remove_selected_guide)
        guides_list_layout.addWidget(remove_guide_btn)

        guides_group.setLayout(guides_list_layout)
        guides_layout.addWidget(guides_group)

        guides_widget.setLayout(guides_layout)
        guides_dock.setWidget(guides_widget)
        self.addDockWidget(Qt.LeftDockWidgetArea, guides_dock)

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

        # File menu
        file_menu = menubar.addMenu("Dosya")

        new_action = QAction("Yeni", self)
        new_action.setShortcut("Ctrl+N")
        new_action.triggered.connect(self.new_file)
        file_menu.addAction(new_action)

        open_action = QAction("Aç...", self)
        open_action.setShortcut("Ctrl+O")
        open_action.triggered.connect(self.open_file)
        file_menu.addAction(open_action)

        # Recent files submenu
        self.recent_menu = file_menu.addMenu("Son Dosyalar")
        self.update_recent_files_menu()

        save_action = QAction("Kaydet", self)
        save_action.setShortcut("Ctrl+S")
        save_action.triggered.connect(self.save_file)
        file_menu.addAction(save_action)

        save_as_action = QAction("Farklı Kaydet...", self)
        save_as_action.triggered.connect(self.save_file_as)
        file_menu.addAction(save_as_action)

        save_project_action = QAction("Projeyi Kaydet", self)
        save_project_action.setShortcut("Ctrl+Shift+S")
        save_project_action.triggered.connect(self.canvas.save_project)
        file_menu.addAction(save_project_action)

        load_project_action = QAction("Proje Aç...", self)
        load_project_action.setShortcut("Ctrl+Shift+O")
        load_project_action.triggered.connect(self.canvas.load_project)
        file_menu.addAction(load_project_action)

        print_action = QAction("Yazdır...", self)
        print_action.setShortcut("Ctrl+P")
        print_action.triggered.connect(self.print_file)
        file_menu.addAction(print_action)

        file_menu.addSeparator()

        export_action = QAction("Dışa Aktar...", self)
        export_action.triggered.connect(self.export_image)
        file_menu.addAction(export_action)

        file_menu.addSeparator()

        exit_action = QAction("Çıkış", self)
        exit_action.setShortcut("Ctrl+Q")
        exit_action.triggered.connect(self.close)
        file_menu.addAction(exit_action)

        # Edit menu
        edit_menu = menubar.addMenu("Düzen")

        undo_action = QAction("Geri Al", self)
        undo_action.setShortcut("Ctrl+Z")
        undo_action.triggered.connect(self.canvas.undo)
        edit_menu.addAction(undo_action)

        redo_action = QAction("Yinele", self)
        redo_action.setShortcut("Ctrl+Y")
        redo_action.triggered.connect(self.canvas.redo)
        edit_menu.addAction(redo_action)

        edit_menu.addSeparator()

        cut_action = QAction("Kes", self)
        cut_action.setShortcut("Ctrl+X")
        cut_action.triggered.connect(self.cut_selection)
        edit_menu.addAction(cut_action)

        copy_action = QAction("Kopyala", self)
        copy_action.setShortcut("Ctrl+C")
        copy_action.triggered.connect(self.copy_selection)
        edit_menu.addAction(copy_action)

        paste_action = QAction("Yapıştır", self)
        paste_action.setShortcut("Ctrl+V")
        paste_action.triggered.connect(self.paste_selection)
        edit_menu.addAction(paste_action)

        delete_action = QAction("Sil", self)
        delete_action.setShortcut("Del")
        delete_action.triggered.connect(self.delete_selection)
        edit_menu.addAction(delete_action)

        edit_menu.addSeparator()

        clear_action = QAction("Temizle", self)
        clear_action.triggered.connect(self.clear_canvas)
        edit_menu.addAction(clear_action)

        edit_menu.addSeparator()

        resize_action = QAction("Tuvali Yeniden Boyutlandır...", self)
        resize_action.triggered.connect(self.resize_canvas)
        edit_menu.addAction(resize_action)

        rotate_action = QAction("Döndür...", self)
        rotate_action.triggered.connect(self.rotate_image)
        edit_menu.addAction(rotate_action)

        flip_h_action = QAction("Yatay Çevir", self)
        flip_h_action.triggered.connect(self.flip_horizontal)
        edit_menu.addAction(flip_h_action)

        flip_v_action = QAction("Dikey Çevir", self)
        flip_v_action.triggered.connect(self.flip_vertical)
        edit_menu.addAction(flip_v_action)

        # View menu
        view_menu = menubar.addMenu("Görünüm")

        zoom_in_action = QAction("Yakınlaştır", self)
        zoom_in_action.setShortcut("Ctrl++")
        zoom_in_action.triggered.connect(self.canvas.zoom_in)
        view_menu.addAction(zoom_in_action)

        zoom_out_action = QAction("Uzaklaştır", self)
        zoom_out_action.setShortcut("Ctrl+-")
        zoom_out_action.triggered.connect(self.canvas.zoom_out)
        view_menu.addAction(zoom_out_action)

        zoom_reset_action = QAction("Normal Boyut", self)
        zoom_reset_action.setShortcut("Ctrl+0")
        zoom_reset_action.triggered.connect(self.canvas.reset_zoom)
        view_menu.addAction(zoom_reset_action)

        view_menu.addSeparator()

        grid_action = QAction("Izgarayı Göster", self, checkable=True)
        grid_action.setShortcut("Ctrl+G")
        grid_action.triggered.connect(self.toggle_grid)
        view_menu.addAction(grid_action)

        ruler_action = QAction("Cetveli Göster", self, checkable=True)
        ruler_action.setShortcut("Ctrl+R")
        ruler_action.triggered.connect(self.toggle_ruler)
        view_menu.addAction(ruler_action)

        # Tools menu
        tools_menu = menubar.addMenu("Araçlar")

        for tool, action in self.tool_actions.items():
            tools_menu.addAction(action)

        tools_menu.addSeparator()

        color_action = QAction("Renk Seç...", self)
        color_action.triggered.connect(self.choose_pen_color)
        tools_menu.addAction(color_action)

        fill_color_action = QAction("Dolgu Rengi Seç...", self)
        fill_color_action.triggered.connect(self.choose_fill_color)
        tools_menu.addAction(fill_color_action)

        font_action = QAction("Yazı Tipi...", self)
        font_action.triggered.connect(self.choose_font)
        tools_menu.addAction(font_action)

        # Filters menu
        filters_menu = menubar.addMenu("Filtreler")

        blur_action = QAction("Bulanıklaştır", self)
        blur_action.triggered.connect(lambda: self.apply_filter("blur"))
        filters_menu.addAction(blur_action)

        sharpen_action = QAction("Keskinleştir", self)
        sharpen_action.triggered.connect(lambda: self.apply_filter("sharpen"))
        filters_menu.addAction(sharpen_action)

        edge_action = QAction("Kenar Bul", self)
        edge_action.triggered.connect(lambda: self.apply_filter("find_edges"))
        filters_menu.addAction(edge_action)

        emboss_action = QAction("Kabartma", self)
        emboss_action.triggered.connect(lambda: self.apply_filter("emboss"))
        filters_menu.addAction(emboss_action)

        filters_menu.addSeparator()

        grayscale_action = QAction("Gri Tonlama", self)
        grayscale_action.triggered.connect(lambda: self.apply_filter("grayscale"))
        filters_menu.addAction(grayscale_action)

        sepia_action = QAction("Sepya", self)
        sepia_action.triggered.connect(lambda: self.apply_filter("sepia"))
        filters_menu.addAction(sepia_action)

        # AI menu
        ai_menu = menubar.addMenu("AI Araçlar")

        load_model_action = QAction("AI Modeli Yükle...", self)
        load_model_action.triggered.connect(self.load_ai_model)
        ai_menu.addAction(load_model_action)

        style_transfer_action = QAction("Stil Transferi", self)
        style_transfer_action.triggered.connect(lambda: self.apply_ai_effect("style_transfer"))
        ai_menu.addAction(style_transfer_action)

        # Window menu
        window_menu = menubar.addMenu("Pencere")

        reset_layout_action = QAction("Düzeni Sıfırla", self)
        reset_layout_action.triggered.connect(self.reset_layout)
        window_menu.addAction(reset_layout_action)

        # Help menu
        help_menu = menubar.addMenu("Yardım")

        about_action = QAction("Hakkında", self)
        about_action.triggered.connect(self.show_about)
        help_menu.addAction(about_action)

        shortcuts_action = QAction("Kısayollar", self)
        shortcuts_action.triggered.connect(self.show_shortcuts)
        help_menu.addAction(shortcuts_action)

    def set_tool(self, tool):
        self.canvas.tool = tool
        for t, action in self.tool_actions.items():
            action.setChecked(t == tool)

        # Update cursor
        if tool == "pen":
            self.canvas.setCursor(QCursor(Qt.CrossCursor))
        elif tool == "text":
            self.canvas.setCursor(QCursor(Qt.IBeamCursor))
        elif tool == "eyedropper":
            self.canvas.setCursor(QCursor(Qt.PointingHandCursor))
        else:
            self.canvas.setCursor(QCursor(Qt.ArrowCursor))

        self.status_bar.showMessage(f"Araç: {self.tool_actions[tool].text()}")

    def choose_pen_color(self):
        color = QColorDialog.getColor(self.canvas.pen_color, self, "Renk Seç")
        if color.isValid():
            self.canvas.pen_color = color
            self.canvas.pen_color.setAlpha(self.canvas.alpha)
            self.update_color_preview()

    def choose_fill_color(self):
        color = QColorDialog.getColor(self.canvas.fill_color, self, "Dolgu Rengi Seç")
        if color.isValid():
            self.canvas.fill_color = color
            self.update_color_preview()

    def update_color_preview(self):
        # Create a preview showing both pen and fill colors
        pixmap = QPixmap(32, 32)
        pixmap.fill(Qt.white)

        painter = QPainter(pixmap)
        painter.setBrush(self.canvas.fill_color)
        painter.setPen(QPen(self.canvas.pen_color, 2))
        painter.drawRect(0, 0, 31, 31)
        painter.end()

        self.color_preview.setPixmap(pixmap)

    def choose_font(self):
        font, ok = QFontDialog.getFont(self.canvas.font, self, "Yazı Tipi Seç")
        if ok:
            self.canvas.font = font

    def set_pen_width(self, width):
        self.canvas.pen_width = width

    def set_alpha(self, alpha):
        self.canvas.alpha = alpha
        self.canvas.pen_color.setAlpha(alpha)
        self.update_color_preview()

    def set_gradient_type(self, index):
        types = ["none", "linear", "radial", "conical"]
        if 0 <= index < len(types):
            self.canvas.gradient_type = types[index]

    def set_brush_style(self, index):
        styles = [Qt.SolidLine, Qt.DashLine, Qt.DotLine, Qt.DashDotLine]
        if 0 <= index < len(styles):
            self.canvas.brush_style = styles[index]

    def set_cap_style(self, index):
        caps = [Qt.RoundCap, Qt.FlatCap, Qt.SquareCap]
        if 0 <= index < len(caps):
            self.canvas.cap_style = caps[index]

    def set_join_style(self, index):
        joins = [Qt.RoundJoin, Qt.BevelJoin, Qt.MiterJoin]
        if 0 <= index < len(joins):
            self.canvas.join_style = joins[index]

    def load_custom_brush(self):
        file_name, _ = QFileDialog.getOpenFileName(self, "Fırça Yükle", "",
                                                   "Resim Dosyaları (*.png *.jpg *.bmp);;Tüm Dosyalar (*)")
        if file_name:
            if self.canvas.load_custom_brush(file_name):
                QMessageBox.information(self, "Başarılı", "Fırça başarıyla yüklendi!")
            else:
                QMessageBox.warning(self, "Hata", "Fırça yüklenemedi!")

    def set_brush_spacing(self, spacing):
        self.canvas.brush_spacing = spacing

    def set_brush_scatter(self, scatter):
        self.canvas.brush_scatter = scatter

    def set_brush_rotation(self, rotation):
        self.canvas.brush_rotation = rotation

    def toggle_symmetry(self, state):
        self.canvas.set_symmetry(state == Qt.Checked)

    def set_symmetry_axis(self, index):
        axes = ["vertical", "horizontal", "point", "multi"]
        if 0 <= index < len(axes):
            self.canvas.symmetry_axis = axes[index]

    def set_symmetry_count(self, count):
        self.canvas.symmetry_count = count

    def toggle_grid(self, checked=None):
        if checked is None:
            self.canvas.show_grid = not self.canvas.show_grid
        else:
            self.canvas.show_grid = checked
        self.canvas.update()

    def toggle_ruler(self, checked=None):
        if checked is None:
            self.canvas.show_ruler = not self.canvas.show_ruler
        else:
            self.canvas.show_ruler = checked
        self.canvas.update()

    def toggle_snap_to_grid(self, state):
        # Implement snap to grid functionality
        pass

    def set_grid_size(self, size):
        self.canvas.set_grid_size(size)

    def add_guide(self, orientation):
        if orientation == "horizontal":
            pos, ok = QInputDialog.getInt(self, "Yatay Kılavuz", "Pozisyon (Y):", 0, 0, self.canvas.layers[0].height())
        else:
            pos, ok = QInputDialog.getInt(self, "Dikey Kılavuz", "Pozisyon (X):", 0, 0, self.canvas.layers[0].width())

        if ok:
            self.canvas.add_guide(orientation, pos)
            self.update_guides_list()

    def remove_selected_guide(self):
        selected = self.guides_list.currentRow()
        if selected >= 0:
            self.canvas.remove_guide(selected)
            self.update_guides_list()

    def update_guides_list(self):
        self.guides_list.clear()
        for guide in self.canvas.guides:
            orientation = "Yatay" if guide["orientation"] == "horizontal" else "Dikey"
            self.guides_list.addItem(f"{orientation}: {guide['position']}")

    def update_layer_list(self):
        self.layer_list.clear()
        for i, layer in enumerate(self.canvas.layers):
            item = QListWidgetItem(f"Katman {i + 1}")
            item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
            item.setCheckState(Qt.Checked if self.canvas.layer_visibility[i] else Qt.Unchecked)
            if i == self.canvas.current_layer_index:
                item.setBackground(QColor(200, 200, 255))
            self.layer_list.addItem(item)

    def select_layer(self, item):
        index = self.layer_list.row(item)
        if 0 <= index < len(self.canvas.layers):
            self.canvas.current_layer_index = index
            self.update_layer_list()

    def remove_current_layer(self):
        self.canvas.remove_layer(self.canvas.current_layer_index)

    def move_layer_up(self):
        index = self.canvas.current_layer_index
        if index < len(self.canvas.layers) - 1:
            self.canvas.move_layer_up(index)

    def move_layer_down(self):
        index = self.canvas.current_layer_index
        if index > 0:
            self.canvas.move_layer_down(index)

    def update_frame_list(self):
        self.frame_list.clear()
        for i, frame in enumerate(self.canvas.frames):
            item = QListWidgetItem(f"Kare {i + 1}")
            if i == self.canvas.current_frame:
                item.setBackground(QColor(200, 200, 255))
            self.frame_list.addItem(item)

    def select_frame(self, item):
        index = self.frame_list.row(item)
        if 0 <= index < len(self.canvas.frames):
            self.canvas.current_frame = index
            self.canvas.update()

    def remove_current_frame(self):
        self.canvas.remove_frame(self.canvas.current_frame)

    def set_animation_speed(self, speed):
        self.canvas.animation_speed = speed

    def new_file(self):
        reply = QMessageBox.question(self, 'Yeni Dosya',
                                     'Geçerli çizimi kaydetmek istiyor musunuz?',
                                     QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)

        if reply == QMessageBox.Yes:
            self.save_file()
            self.create_new_file()
        elif reply == QMessageBox.No:
            self.create_new_file()

    def create_new_file(self):
        width, ok1 = QInputDialog.getInt(self, 'Yeni Dosya', 'Genişlik:', 800, 100, 5000)
        height, ok2 = QInputDialog.getInt(self, 'Yeni Dosya', 'Yükseklik:', 600, 100, 5000)

        if ok1 and ok2:
            self.canvas.save_undo_state()
            self.canvas.layers = [QImage(QSize(width, height), QImage.Format_ARGB32)]
            self.canvas.layers[0].fill(Qt.white)
            self.canvas.layer_visibility = [True]
            self.canvas.current_layer_index = 0
            self.canvas.shapes = []
            self.canvas.frames = [self.canvas.layers[0].copy()]
            self.canvas.current_frame = 0
            self.update_layer_list()
            self.update_frame_list()
            self.canvas.update()

    def open_file(self):
        options = QFileDialog.Options()
        file_name, _ = QFileDialog.getOpenFileName(self, "Resim Aç", "",
                                                   "Resim Dosyaları (*.png *.jpg *.jpeg *.bmp);;Tüm Dosyalar (*)",
                                                   options=options)
        if file_name:
            self.add_recent_file(file_name)
            image = QImage(file_name)
            if not image.isNull():
                self.canvas.save_undo_state()
                self.canvas.layers = [image.convertToFormat(QImage.Format_ARGB32)]
                self.canvas.layer_visibility = [True]
                self.canvas.current_layer_index = 0
                self.canvas.shapes = []
                self.canvas.frames = [self.canvas.layers[0].copy()]
                self.canvas.current_frame = 0
                self.update_layer_list()
                self.update_frame_list()
                self.status_bar.showMessage(f"Açılan dosya: {file_name}")
                self.current_file = file_name
            else:
                QMessageBox.warning(self, "Hata", "Dosya açılamadı veya desteklenmeyen format!")

    def save_file(self):
        if hasattr(self, 'current_file') and self.current_file:
            if self.canvas.layers[0].save(self.current_file):
                self.status_bar.showMessage(f"Kaydedildi: {self.current_file}")
            else:
                QMessageBox.warning(self, "Hata", "Dosya kaydedilemedi!")
        else:
            self.save_file_as()

    def save_file_as(self):
        options = QFileDialog.Options()
        file_name, _ = QFileDialog.getSaveFileName(self, "Resmi Kaydet", "",
                                                   "PNG (*.png);;JPEG (*.jpg *.jpeg);;Bitmap (*.bmp);;Tüm Dosyalar (*)",
                                                   options=options)
        if file_name:
            self.add_recent_file(file_name)
            self.current_file = file_name
            if self.canvas.layers[0].save(file_name):
                self.status_bar.showMessage(f"Kaydedildi: {file_name}")
            else:
                QMessageBox.warning(self, "Hata", "Dosya kaydedilemedi!")

    def export_image(self):
        options = QFileDialog.Options()
        file_name, _ = QFileDialog.getSaveFileName(self, "Dışa Aktar", "",
                                                   "PNG (*.png);;JPEG (*.jpg *.jpeg);;Bitmap (*.bmp);;Tüm Dosyalar (*)",
                                                   options=options)
        if file_name:
            # Merge all visible layers before exporting
            merged_image = QImage(self.canvas.layers[0].size(), QImage.Format_ARGB32)
            merged_image.fill(Qt.transparent)

            painter = QPainter(merged_image)
            for i, layer in enumerate(self.canvas.layers):
                if self.canvas.layer_visibility[i]:
                    painter.drawImage(0, 0, layer)
            painter.end()

            if merged_image.save(file_name):
                self.status_bar.showMessage(f"Dışa aktarıldı: {file_name}")
            else:
                QMessageBox.warning(self, "Hata", "Dosya kaydedilemedi!")

    def print_file(self):
        printer = QPrinter()
        dialog = QPrintDialog(printer, self)
        if dialog.exec_() == QPrintDialog.Accepted:
            # Merge all visible layers before printing
            merged_image = QImage(self.canvas.layers[0].size(), QImage.Format_ARGB32)
            merged_image.fill(Qt.transparent)

            painter = QPainter(merged_image)
            for i, layer in enumerate(self.canvas.layers):
                if self.canvas.layer_visibility[i]:
                    painter.drawImage(0, 0, layer)
            painter.end()

            # Print the merged image
            painter = QPainter(printer)
            rect = painter.viewport()
            size = merged_image.size()
            size.scale(rect.size(), Qt.KeepAspectRatio)
            painter.setViewport(rect.x(), rect.y(), size.width(), size.height())
            painter.setWindow(merged_image.rect())
            painter.drawImage(0, 0, merged_image)
            painter.end()

    def clear_canvas(self):
        reply = QMessageBox.question(self, 'Temizle',
                                     'Tuvali temizlemek istediğinize emin misiniz?',
                                     QMessageBox.Yes | QMessageBox.No)

        if reply == QMessageBox.Yes:
            self.canvas.clear_canvas()

    def resize_canvas(self):
        width, ok1 = QInputDialog.getInt(self, 'Tuvali Yeniden Boyutlandır', 'Genişlik:',
                                         self.canvas.layers[0].width(), 100, 5000)
        height, ok2 = QInputDialog.getInt(self, 'Tuvali Yeniden Boyutlandır', 'Yükseklik:',
                                          self.canvas.layers[0].height(), 100, 5000)

        if ok1 and ok2:
            self.canvas.save_undo_state()
            self.canvas.resize_image(width, height)

    def rotate_image(self):
        angle, ok = QInputDialog.getInt(self, 'Resmi Döndür', 'Derece (0-360):', 0, 0, 360)
        if ok:
            self.canvas.save_undo_state()
            self.canvas.rotate_image(angle)

    def flip_horizontal(self):
        self.canvas.save_undo_state()
        self.canvas.flip_horizontal()

    def flip_vertical(self):
        self.canvas.save_undo_state()
        self.canvas.flip_vertical()

    def cut_selection(self):
        if self.canvas.selected_area:
            self.copy_selection()
            self.delete_selection()

    def copy_selection(self):
        if self.canvas.selected_area:
            clipboard = QApplication.clipboard()
            clipboard.setImage(self.canvas.selected_area)

    def paste_selection(self):
        clipboard = QApplication.clipboard()
        image = clipboard.image()

        if not image.isNull():
            self.canvas.save_undo_state()
            self.canvas.selected_area = image
            self.canvas.selection_start = QPoint(10, 10)
            self.canvas.selection_end = QPoint(10 + image.width(), 10 + image.height())
            self.canvas.tool = "move"
            self.tool_actions["move"].setChecked(True)
            self.canvas.update()

    def delete_selection(self):
        if self.canvas.selected_area:
            self.canvas.save_undo_state()

            painter = QPainter(self.canvas.layers[self.canvas.current_layer_index])
            painter.setCompositionMode(QPainter.CompositionMode_Clear)
            rect = QRect(self.canvas.selection_start, self.canvas.selected_area.size())
            painter.fillRect(rect, Qt.transparent)
            painter.end()

            self.canvas.selected_area = None
            self.canvas.update()

    def apply_filter(self, filter_name):
        self.canvas.save_undo_state()
        self.canvas.apply_filter(filter_name)

    def load_ai_model(self):
        file_name, _ = QFileDialog.getOpenFileName(self, "AI Modeli Yükle", "",
                                                   "Model Dosyaları (*.h5 *.pt *.pth);;Tüm Dosyalar (*)")
        if file_name:
            if self.canvas.load_ai_model(file_name):
                QMessageBox.information(self, "Başarılı", "AI modeli başarıyla yüklendi!")
            else:
                QMessageBox.warning(self, "Hata", "Model yüklenemedi!")

    def apply_ai_effect(self, effect_name):
        if not self.canvas.ai_model_loaded:
            QMessageBox.warning(self, "Hata", "Önce bir AI modeli yüklemelisiniz!")
            return

        if self.canvas.apply_ai_effect(effect_name):
            QMessageBox.information(self, "Başarılı", "AI efekti uygulandı!")
        else:
            QMessageBox.warning(self, "Hata", "AI efekti uygulanamadı!")

    def reset_layout(self):
        # Reset dock widgets to default positions
        self.setTabPosition(Qt.AllDockWidgetAreas, QTabWidget.North)

        for dock in self.findChildren(QDockWidget):
            dock.setFloating(False)

        self.resizeDocks(
            [self.findChild(QDockWidget, "Katmanlar"), self.findChild(QDockWidget, "Animasyon")],
            [200, 200],
            Qt.Horizontal
        )

    def show_about(self):
        QMessageBox.about(self, "Hakkında",
                          "Süper Gelişmiş Paint Uygulaması\n\n"
                          "Python ve PyQt5 ile geliştirilmiştir.\n"
                          "© 2023\n\n"
                          "Versiyon: 2.0")

    def show_shortcuts(self):
        shortcuts = [
            ("Ctrl+N", "Yeni dosya"),
            ("Ctrl+O", "Dosya aç"),
            ("Ctrl+S", "Kaydet"),
            ("Ctrl+Shift+S", "Projeyi kaydet"),
            ("Ctrl+Shift+O", "Proje aç"),
            ("Ctrl+P", "Yazdır"),
            ("Ctrl+Z", "Geri al"),
            ("Ctrl+Y", "Yinele"),
            ("Ctrl+X", "Kes"),
            ("Ctrl+C", "Kopyala"),
            ("Ctrl+V", "Yapıştır"),
            ("Del", "Sil"),
            ("Ctrl+G", "Izgarayı göster/gizle"),
            ("Ctrl+R", "Cetveli göster/gizle"),
            ("Ctrl++", "Yakınlaştır"),
            ("Ctrl+-", "Uzaklaştır"),
            ("Ctrl+0", "Normal boyut"),
            ("Ctrl+Q", "Çıkış")
        ]

        message = "Klavye Kısayolları:\n\n"
        for shortcut, description in shortcuts:
            message += f"{shortcut}: {description}\n"

        QMessageBox.information(self, "Kısayollar", message)

    def add_recent_file(self, file_path):
        if file_path in self.recent_files:
            self.recent_files.remove(file_path)

        self.recent_files.insert(0, file_path)

        if len(self.recent_files) > self.max_recent_files:
            self.recent_files = self.recent_files[:self.max_recent_files]

        self.update_recent_files_menu()
        self.save_settings()

    def update_recent_files_menu(self):
        self.recent_menu.clear()

        for i, file_path in enumerate(self.recent_files[:self.max_recent_files]):
            action = QAction(f"{i + 1}. {os.path.basename(file_path)}", self)
            action.setData(file_path)
            action.triggered.connect(lambda _, f=file_path: self.open_recent_file(f))
            self.recent_menu.addAction(action)

        self.recent_menu.addSeparator()
        clear_action = QAction("Listeyi Temizle", self)
        clear_action.triggered.connect(self.clear_recent_files)
        self.recent_menu.addAction(clear_action)

    def open_recent_file(self, file_path):
        if os.path.exists(file_path):
            self.open_file(file_path)
        else:
            QMessageBox.warning(self, "Hata", "Dosya bulunamadı!")
            self.recent_files.remove(file_path)
            self.update_recent_files_menu()

    def clear_recent_files(self):
        self.recent_files = []
        self.update_recent_files_menu()
        self.save_settings()

    def save_settings(self):
        settings = {
            "recent_files": self.recent_files,
            "window_geometry": self.saveGeometry(),
            "window_state": self.saveState(),
            "pen_color": self.canvas.pen_color.name(),
            "fill_color": self.canvas.fill_color.name(),
            "pen_width": self.canvas.pen_width,
            "alpha": self.canvas.alpha,
            "grid_visible": self.canvas.show_grid,
            "ruler_visible": self.canvas.show_ruler
        }

        with open("paint_settings.json", "w") as f:
            json.dump(settings, f)

    def load_settings(self):
        try:
            with open("paint_settings.json", "r") as f:
                settings = json.load(f)

                self.recent_files = settings.get("recent_files", [])
                self.update_recent_files_menu()

                if "window_geometry" in settings:
                    self.restoreGeometry(settings["window_geometry"])

                if "window_state" in settings:
                    self.restoreState(settings["window_state"])

                if "pen_color" in settings:
                    self.canvas.pen_color = QColor(settings["pen_color"])

                if "fill_color" in settings:
                    self.canvas.fill_color = QColor(settings["fill_color"])

                if "pen_width" in settings:
                    self.canvas.pen_width = settings["pen_width"]
                    self.width_spin.setValue(self.canvas.pen_width)

                if "alpha" in settings:
                    self.canvas.alpha = settings["alpha"]
                    self.alpha_spin.setValue(self.canvas.alpha)

                if "grid_visible" in settings:
                    self.canvas.show_grid = settings["grid_visible"]
                    self.toggle_grid(self.canvas.show_grid)

                if "ruler_visible" in settings:
                    self.canvas.show_ruler = settings["ruler_visible"]
                    self.toggle_ruler(self.canvas.show_ruler)

                self.update_color_preview()

        except (FileNotFoundError, json.JSONDecodeError):
            pass

    def closeEvent(self, event):
        reply = QMessageBox.question(self, 'Çıkış',
                                     'Değişiklikleri kaydetmek istiyor musunuz?',
                                     QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)

        if reply == QMessageBox.Yes:
            self.save_file()
            self.save_settings()
            event.accept()
        elif reply == QMessageBox.No:
            self.save_settings()
            event.accept()
        else:
            event.ignore()

    def update_undo_redo_buttons(self):
        self.undo_action.setEnabled(len(self.canvas.undo_stack) > 0)
        self.redo_action.setEnabled(len(self.canvas.redo_stack) > 0)


if __name__ == "__main__":
    # Create icons directory if not exists
    if not os.path.exists("icons"):
        os.makedirs("icons")
        # You would need to add your icon files here

    app = QApplication(sys.argv)
    window = PaintApp()
    window.show()
    sys.exit(app.exec_())
Osman Bayrak
Osman Bayrak

Yazılım Mühendisiyim. Teknoloji ve yazılıma meraklıyım.

Articles: 334