From 751aebe26e52c120e3ac5458467379e65f2fb40b Mon Sep 17 00:00:00 2001 From: Rebecca Breu Date: Tue, 30 Mar 2021 20:51:58 +0200 Subject: [PATCH] Bring selected item to front; load/save z values --- beeref/fileio/schema.py | 5 +++-- beeref/fileio/sql.py | 25 +++++++++++++------------ beeref/items.py | 16 ++++++++++++++++ beeref/scene.py | 1 + tests/fileio/test_sql.py | 40 ++++++++++++++++++++++++---------------- tests/test_items.py | 28 ++++++++++++++++++++++++++++ 6 files changed, 85 insertions(+), 30 deletions(-) diff --git a/beeref/fileio/schema.py b/beeref/fileio/schema.py index 13b6a84..b6f7104 100644 --- a/beeref/fileio/schema.py +++ b/beeref/fileio/schema.py @@ -3,8 +3,9 @@ SCHEMA = [ CREATE TABLE items ( id INTEGER PRIMARY KEY, type TEXT NOT NULL, - pos_x REAL DEFAULT 0, - pos_y REAL DEFAULT 0, + x REAL DEFAULT 0, + y REAL DEFAULT 0, + z REAL DEFAULT 0, scale REAL DEFAULT 1, rotation REAL DEFAULT 0, flip_h INTEGER DEFAULT 0, diff --git a/beeref/fileio/sql.py b/beeref/fileio/sql.py index 87d4a96..8c6bf59 100644 --- a/beeref/fileio/sql.py +++ b/beeref/fileio/sql.py @@ -127,19 +127,20 @@ class SQLiteIO: @handle_sqlite_errors def read(self): rows = self.fetchall( - 'SELECT pos_x, pos_y, scale, filename, sqlar.data, items.id ' + 'SELECT items.id, x, y, z, scale, filename, sqlar.data ' 'FROM items ' 'INNER JOIN sqlar on sqlar.item_id = items.id') if self.progress: self.progress.setMaximum(len(rows)) for i, row in enumerate(rows): - item = BeePixmapItem(QtGui.QImage(), filename=row[3]) - item.save_id = row[5] - item.pixmap_from_bytes(row[4]) - item.setPos(row[0], row[1]) - item.setScale(row[2]) + item = BeePixmapItem(QtGui.QImage(), filename=row[5]) + item.save_id = row[0] + item.pixmap_from_bytes(row[6]) + item.setPos(row[1], row[2]) self.scene.addItem(item) + item.setZValue(row[3]) + item.setScale(row[4]) if self.progress: self.progress.setValue(i) if self.progress.wasCanceled(): @@ -186,10 +187,10 @@ class SQLiteIO: def insert_item(self, item): self.ex( - 'INSERT INTO items (type, pos_x, pos_y, scale, filename) ' - 'VALUES (?, ?, ?, ?, ?) ', - ('pixmap', item.pos().x(), item.pos().y(), item.scale(), - item.filename)) + 'INSERT INTO items (type, x, y, z, scale, filename) ' + 'VALUES (?, ?, ?, ?, ?, ?) ', + ('pixmap', item.pos().x(), item.pos().y(), item.zValue(), + item.scale(), item.filename)) item.save_id = self.cursor.lastrowid pixmap = item.pixmap_to_bytes() @@ -212,8 +213,8 @@ class SQLiteIO: data never changes and is also time-consuming to save. """ self.ex( - 'UPDATE items SET pos_x=?, pos_y=?, scale=?, filename=? ' + 'UPDATE items SET x=?, y=?, z=?, scale=?, filename=? ' 'WHERE id=?', - (item.pos().x(), item.pos().y(), item.scale(), + (item.pos().x(), item.pos().y(), item.zValue(), item.scale(), item.filename, item.save_id)) self.connection.commit() diff --git a/beeref/items.py b/beeref/items.py index 60acd47..c98269a 100644 --- a/beeref/items.py +++ b/beeref/items.py @@ -21,6 +21,7 @@ import logging from PyQt6 import QtCore, QtGui, QtWidgets from PyQt6.QtCore import Qt +from PyQt6.QtWidgets import QGraphicsItem from beeref import commands @@ -65,6 +66,13 @@ class BeePixmapItem(QtWidgets.QGraphicsPixmapItem): self.prepareGeometryChange() super().setScale(factor) + def setZValue(self, value): + super().setZValue(value) + self.scene().max_z = max(self.scene().max_z, value) + + def bring_to_front(self): + self.setZValue(self.scene().max_z + 0.001) + def set_pos_center(self, x, y): """Sets the position using the item's center as the origin point.""" @@ -79,6 +87,14 @@ class BeePixmapItem(QtWidgets.QGraphicsPixmapItem): def height(self): return self.pixmap().size().height() + def itemChange(self, change, value): + if (change == QGraphicsItem.GraphicsItemChange.ItemSelectedChange + and value + and self.scene() + and not self.scene().has_selection()): + self.bring_to_front() + return super().itemChange(change, value) + def pixmap_to_bytes(self): """Convert the pixmap data to PNG bytestring.""" barray = QtCore.QByteArray() diff --git a/beeref/scene.py b/beeref/scene.py index e911274..92bd1ff 100644 --- a/beeref/scene.py +++ b/beeref/scene.py @@ -31,6 +31,7 @@ class BeeGraphicsScene(QtWidgets.QGraphicsScene): self.move_active = False self.undo_stack = undo_stack self.selectionChanged.connect(self.on_selection_changed) + self.max_z = 0 def normalize_width_or_height(self, mode): """Scale the selected images to have the same width or height, as diff --git a/tests/fileio/test_sql.py b/tests/fileio/test_sql.py index 928f847..afa3e9a 100644 --- a/tests/fileio/test_sql.py +++ b/tests/fileio/test_sql.py @@ -88,25 +88,28 @@ class SQLiteIOWriteTestCase(BeeTestCase): def test_inserts_new_item(self): item = BeePixmapItem(QtGui.QImage(), filename='bee.jpg') + self.scene.addItem(item) item.setScale(1.3) item.setPos(44, 55) + item.setZValue(0.22) item.pixmap_to_bytes = MagicMock(return_value=b'abc') self.scene.addItem(item) self.io.write() assert item.save_id == 1 result = self.io.fetchone( - 'SELECT pos_x, pos_y, scale, filename, type, ' + 'SELECT x, y, z, scale, filename, type, ' 'sqlar.data, sqlar.name ' 'FROM items ' 'INNER JOIN sqlar on sqlar.item_id = items.id') assert result[0] == 44.0 assert result[1] == 55.0 - assert result[2] == 1.3 - assert result[3] == 'bee.jpg' - assert result[4] == 'pixmap' - assert result[5] == b'abc' - assert result[6] == '0001-bee.png' + assert result[2] == 0.22 + assert result[3] == 1.3 + assert result[4] == 'bee.jpg' + assert result[5] == 'pixmap' + assert result[6] == b'abc' + assert result[7] == '0001-bee.png' def test_inserts_new_item_without_filename(self): item = BeePixmapItem(QtGui.QImage()) @@ -122,14 +125,17 @@ class SQLiteIOWriteTestCase(BeeTestCase): def test_updates_existing_item(self): item = BeePixmapItem(QtGui.QImage(), filename='bee.png') + self.scene.addItem(item) item.setScale(1.3) item.setPos(44, 55) + item.setZValue(0.22) item.save_id = 1 self.scene.addItem(item) item.pixmap_to_bytes = MagicMock(return_value=b'abc') self.io.write() item.setScale(0.7) item.setPos(20, 30) + item.setZValue(0.33) item.filename = 'new.png' item.pixmap_to_bytes.return_value = b'updated' self.io.create_new = False @@ -137,14 +143,15 @@ class SQLiteIOWriteTestCase(BeeTestCase): assert self.io.fetchone('SELECT COUNT(*) from items') == (1,) result = self.io.fetchone( - 'SELECT pos_x, pos_y, scale, filename, sqlar.data ' + 'SELECT x, y, z, scale, filename, sqlar.data ' 'FROM items ' 'INNER JOIN sqlar on sqlar.item_id = items.id') assert result[0] == 20 assert result[1] == 30 - assert result[2] == 0.7 - assert result[3] == 'new.png' - assert result[4] == b'abc' + assert result[2] == 0.33 + assert result[3] == 0.7 + assert result[4] == 'new.png' + assert result[5] == b'abc' def test_removes_nonexisting_item(self): item = BeePixmapItem(QtGui.QImage(), filename='bee.png') @@ -195,9 +202,9 @@ class SQLiteIOReadTestCase(BeeTestCase): fname = os.path.join(dirname, 'test.bee') io = SQLiteIO(fname, self.scene, create_new=True) io.create_schema_on_new() - io.ex('INSERT INTO items (type, pos_x, pos_y, scale, filename) ' - 'VALUES (?, ?, ?, ?, ?) ', - ('pixmap', 22.2, 33.3, 3.4, 'bee.png')) + io.ex('INSERT INTO items (type, x, y, z, scale, filename) ' + 'VALUES (?, ?, ?, ?, ?, ?) ', + ('pixmap', 22.2, 33.3, 0.22, 3.4, 'bee.png')) io.ex('INSERT INTO sqlar (item_id, data) VALUES (?, ?)', (1, self.imgdata3x3)) io.connection.commit() @@ -210,6 +217,7 @@ class SQLiteIOReadTestCase(BeeTestCase): assert item.save_id == 1 assert item.pos().x() == 22.2 assert item.pos().y() == 33.3 + assert item.zValue() == 0.22 assert item.scale() == 3.4 assert item.filename == 'bee.png' assert item.width == 3 @@ -221,9 +229,9 @@ class SQLiteIOReadTestCase(BeeTestCase): progress=progress) io.create_schema_on_new() - io.ex('INSERT INTO items (type, pos_x, pos_y, scale, filename) ' - 'VALUES (?, ?, ?, ?, ?) ', - ('pixmap', 0, 0, 1, 'bee.png')) + io.ex('INSERT INTO items (type, x, y, z, scale, filename) ' + 'VALUES (?, ?, ?, ?, ?, ?) ', + ('pixmap', 0, 0, 0, 1, 'bee.png')) io.ex('INSERT INTO sqlar (item_id, data) VALUES (?, ?)', (1, b'')) io.connection.commit() io.read() diff --git a/tests/test_items.py b/tests/test_items.py index b6be078..57676b9 100644 --- a/tests/test_items.py +++ b/tests/test_items.py @@ -9,6 +9,9 @@ from .base import BeeTestCase class BeePixmapItemTestCase(BeeTestCase): + def setUp(self): + self.scene = BeeGraphicsScene(None) + def test_init(self): item = BeePixmapItem( QtGui.QImage(self.imgfilename3x3), self.imgfilename3x3) @@ -48,6 +51,31 @@ class BeePixmapItemTestCase(BeeTestCase): assert item.pos().x() == -100 assert item.pos().y() == -50 + def test_set_zvalue_sets_new_max(self): + item = BeePixmapItem(QtGui.QImage()) + self.scene.addItem(item) + item.setZValue(1.1) + assert item.zValue() == 1.1 + assert self.scene.max_z == 1.1 + + def test_set_zvalue_keeps_old_max(self): + item = BeePixmapItem(QtGui.QImage()) + self.scene.addItem(item) + self.scene.max_z = 3.3 + item.setZValue(1.1) + assert item.zValue() == 1.1 + assert self.scene.max_z == 3.3 + + def test_bring_to_front(self): + item1 = BeePixmapItem(QtGui.QImage()) + self.scene.addItem(item1) + item1.setZValue(3.3) + item2 = BeePixmapItem(QtGui.QImage()) + self.scene.addItem(item2) + item2.bring_to_front() + assert item2.zValue() > item1.zValue() + assert item2.zValue() == self.scene.max_z + class BeePixmapItemPaintstuffTestCase(BeeTestCase):