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:
- Tamamen ücretsiz ve açık kaynak kodlu
- Hafif ve hızlı çalışan bir arayüz
- Photoshop benzeri katman desteği
- Temel ve ileri düzey çizim araçları
- 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:
- Dijital sanat çalışmaları oluşturabilir
- Web tasarımı için basit grafikler hazırlayabilir
- Eğitim amaçlı çizimler yapabilir
- Basit animasyonlar oluşturabilir
- Fotoğraf düzenleme işlemleri gerçekleştirebilirsiniz
Kurulum ve Kullanım
Uygulamayı bilgisayarınıza kurmak için:
- Gerekli kütüphaneleri yükleyin:
pip install PyQt5 numpy pillow
- Kodu bir Python dosyasına kaydedin (örneğin
super_paint.py
) - 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_())