Make bee file sqlar compliant

This commit is contained in:
Rebecca Breu 2021-03-27 15:37:03 +01:00
parent ef086f1468
commit e42ed2d598
5 changed files with 64 additions and 39 deletions

View file

@ -27,3 +27,5 @@ Run unittests with coverage report::
coverage html
If your browser doesn't open automatically, view ``htmlcov/index.html``.
Beeref files are sqlite databases, so they can be inspected with any sqlite browser.

View file

@ -24,6 +24,18 @@ At the moment, you need to have a working Python 3 environment to install BeeRef
Then run ``beeref`` or ``beeref filename.bee``.
Regarding the bee file format
-----------------------------
Currently, all images are embedded into the bee file as png files. While png is a lossless format, it may also produce larger file sizes than compressed jpg files, so bee files may become bigger than the imported images. More embedding options are to come later.
The bee file format is a sqlite database inside which the images are stored in an sqlar table—meaning they can be extracted with the `sqlite command line program <https://www.sqlite.org/cli.html>`_::
sqlite3 myfile.bee -Axv
Options for exporting from inside BeeRef are planned, but the above always works independently of BeeRef.
Notes for developers
--------------------

29
beeref/fileio/schema.py Normal file
View file

@ -0,0 +1,29 @@
SCHEMA = [
"""
CREATE TABLE items (
id INTEGER PRIMARY KEY,
type TEXT NOT NULL,
pos_x REAL DEFAULT 0,
pos_y REAL DEFAULT 0,
scale REAL DEFAULT 1,
rotation REAL DEFAULT 0,
flip_h INTEGER DEFAULT 0,
flip_v INTEGER DEFAULT 0,
filename TEXT
)
""",
"""
CREATE TABLE sqlar (
name TEXT PRIMARY KEY,
item_id INTEGER NOT NULL,
mode INT,
mtime INT default current_timestamp,
sz INT,
data BLOB,
FOREIGN KEY (item_id)
REFERENCES items (id)
ON DELETE CASCADE
ON UPDATE NO ACTION
)
""",
]

View file

@ -13,9 +13,14 @@
# You should have received a copy of the GNU General Public License
# along with BeeRef. If not, see <https://www.gnu.org/licenses/>.
"""BeeRef's native file format is using SQLite. For more info, see:
"""BeeRef's native file format is using SQLite. Embedded files are
stored in an sqlar table so that they can be extracted using sqlite's
archive command line option.
For more info, see:
https://www.sqlite.org/appfileformat.html
https://www.sqlite.org/sqlar.html
"""
import os
@ -24,34 +29,7 @@ import sqlite3
from PyQt6 import QtGui
from beeref.items import BeePixmapItem
SCHEMA = [
"""
CREATE TABLE items (
id INTEGER PRIMARY KEY,
type TEXT NOT NULL,
pos_x REAL DEFAULT 0,
pos_y REAL DEFAULT 0,
scale REAL DEFAULT 1,
rotation REAL DEFAULT 0,
flip_h INTEGER DEFAULT 0,
flip_v INTEGER DEFAULT 0,
filename TEXT
)
""",
"""
CREATE TABLE imgdata (
id INTEGER PRIMARY KEY,
item_id INTEGER NOT NULL,
data BLOB,
FOREIGN KEY (item_id)
REFERENCES items (id)
ON DELETE CASCADE
ON UPDATE NO ACTION
)
""",
]
from .schema import SCHEMA
class SQLiteIO:
@ -94,9 +72,9 @@ class SQLiteIO:
def read(self):
rows = self.fetchall(
'SELECT pos_x, pos_y, scale, filename, imgdata.data, items.id '
'SELECT pos_x, pos_y, scale, filename, sqlar.data, items.id '
'FROM items '
'INNER JOIN imgdata on imgdata.item_id = items.id')
'INNER JOIN sqlar on sqlar.item_id = items.id')
for row in rows:
item = BeePixmapItem(QtGui.QImage(), filename=row[3])
item.save_id = row[5]
@ -130,8 +108,12 @@ class SQLiteIO:
('pixmap', item.pos().x(), item.pos().y(), item.scale_factor,
item.filename))
item.save_id = self.cursor.lastrowid
self.ex('INSERT INTO imgdata (item_id, data) VALUES (?, ?)',
(item.save_id, item.pixmap_to_bytes()))
pixmap = item.pixmap_to_bytes()
name = '%04d.png' % item.save_id
self.ex(
'INSERT INTO sqlar (item_id, name, mode, sz, data) '
'VALUES (?, ?, 644, ?, ?)',
(item.save_id, name, len(pixmap), pixmap))
self.connection.commit()
def update_item(self, item):

View file

@ -75,9 +75,9 @@ class SQLiteIOWriteTestCase(BeeTestCase):
assert item.save_id == 1
result = self.io.fetchone(
'SELECT pos_x, pos_y, scale, filename, imgdata.data, type '
'SELECT pos_x, pos_y, scale, filename, sqlar.data, type '
'FROM items '
'INNER JOIN imgdata on imgdata.item_id = items.id')
'INNER JOIN sqlar on sqlar.item_id = items.id')
assert result[0] == 44.0
assert result[1] == 55.0
assert result[2] == 1.3
@ -103,9 +103,9 @@ class SQLiteIOWriteTestCase(BeeTestCase):
assert self.io.fetchone('SELECT COUNT(*) from items') == (1,)
result = self.io.fetchone(
'SELECT pos_x, pos_y, scale, filename, imgdata.data '
'SELECT pos_x, pos_y, scale, filename, sqlar.data '
'FROM items '
'INNER JOIN imgdata on imgdata.item_id = items.id')
'INNER JOIN sqlar on sqlar.item_id = items.id')
assert result[0] == 20
assert result[1] == 30
assert result[2] == 0.7
@ -124,7 +124,7 @@ class SQLiteIOWriteTestCase(BeeTestCase):
self.io.write()
assert self.io.fetchone('SELECT COUNT(*) from items') == (0,)
assert self.io.fetchone('SELECT COUNT(*) from imgdata') == (0,)
assert self.io.fetchone('SELECT COUNT(*) from sqlar') == (0,)
class SQLiteIOLOadTestCase(BeeTestCase):
@ -143,7 +143,7 @@ class SQLiteIOLOadTestCase(BeeTestCase):
'INSERT INTO items (type, pos_x, pos_y, scale, filename) '
'VALUES (?, ?, ?, ?, ?) ',
('pixmap', 22.2, 33.3, 3.4, 'bee.png'))
self.io.ex('INSERT INTO imgdata (item_id, data) VALUES (?, ?)',
self.io.ex('INSERT INTO sqlar (item_id, data) VALUES (?, ?)',
(1, imgdata))
self.io.read()
assert len(self.scene.items()) == 1