Skip to content

Commit

Permalink
Merge pull request #2 from int-brain-lab/develop
Browse files Browse the repository at this point in the history
0.3.0
  • Loading branch information
bimac authored Nov 28, 2024
2 parents ed30c38 + 4e13327 commit 678114c
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 3 deletions.
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).


## [0.3.0] - 2024-10-08

### Added

- core.FileWatcher: watch a file for changes

## [0.2.0] - 2024-10-08

### Added
Expand Down Expand Up @@ -49,7 +55,8 @@ _First release._
- core.ColoredDataFrameTableModel: An extension of DataFrameTableModel providing color-mapped numerical data.
- widgets.StatefulButton: A QPushButton that maintains an active/inactive state.

[0.3.0]: https://github.com/int-brain-lab/iblqt/releases/tag/v0.3.0
[0.2.0]: https://github.com/int-brain-lab/iblqt/releases/tag/v0.2.0
[0.1.2]: https://github.com/int-brain-lab/iblqt/releases/tag/v0.1.2
[0.1.1]: https://github.com/int-brain-lab/iblqt/releases/tag/v0.1.1
[0.1.0]: https://github.com/int-brain-lab/iblqt/releases/tag/v0.1.0
[0.1.0]: https://github.com/int-brain-lab/iblqt/releases/tag/v0.1.0
2 changes: 1 addition & 1 deletion iblqt/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""A collection of extensions to the Qt framework."""

__version__ = '0.2.0'
__version__ = '0.3.0'
48 changes: 48 additions & 0 deletions iblqt/core.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
"""Non-GUI functionality, including event handling, data types, and data management."""

import logging
from pathlib import Path
from typing import Any

from pyqtgraph import ColorMap, colormap # type: ignore
from qtpy.QtCore import (
QAbstractTableModel,
QFileSystemWatcher,
Qt,
QModelIndex,
QReadWriteLock,
QObject,
Property,
Signal,
Expand Down Expand Up @@ -410,3 +413,48 @@ def data(self, index: QModelIndex, role: int = Qt.ItemDataRole.DisplayRole) -> A
lum = self._foreground[row][col]
return QColor('black' if (lum * self._alpha) < 32512 else 'white')
return super().data(index, role)


class FileWatcher(QObject):
"""Watch a file for changes."""

fileChanged = Signal() # type: Signal
"""Emitted when the file's content has changed."""

fileSizeChanged = Signal(int) # type: Signal
"""Emitted when the file's size has changed. The signal carries the new size."""

def __init__(self, parent: QObject, file: Path | str):
"""Initialize the FileWatcher.
Parameters
----------
parent : QObject
The parent object.
file : Path or str
The path to the file to watch.
"""
super().__init__(parent=parent)
self._file = Path(file)
if not self._file.exists():
raise FileNotFoundError(self._file)
if self._file.is_dir():
raise IsADirectoryError(self._file)

self._size = self._file.stat().st_size
self._lock = QReadWriteLock()
self._fileWatcher = QFileSystemWatcher([str(file)], parent)
self._fileWatcher.fileChanged.connect(self._onFileChanged)

@Slot(str)
def _onFileChanged(self, _):
self.fileChanged.emit()
new_size = self._file.stat().st_size

self._lock.lockForWrite()
try:
if new_size != self._size:
self.fileSizeChanged.emit(new_size)
self._size = new_size
finally:
self._lock.unlock()
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,3 @@ files = ["iblqt/**/*.py", "tests/**/*.py"]
addopts = "--cov=iblqt --cov-report=html --cov-report=xml"
minversion = "6.0"
testpaths = [ "tests" ]
qt_api = 'pyqt5'
28 changes: 28 additions & 0 deletions tests/test_core.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from pathlib import Path

import pytest
from qtpy.QtCore import Qt, QModelIndex
from iblqt import core
import tempfile

import pandas as pd

Expand Down Expand Up @@ -72,3 +76,27 @@ def test_dataframe_model(qtbot):
assert model.alpha == 128
assert model.data(model.index(0, 0), Qt.ItemDataRole.BackgroundRole).alpha() == 128
assert model.data(model.index(2, 0), Qt.ItemDataRole.BackgroundRole).alpha() == 128


def test_fileWatcher(qtbot):
with tempfile.NamedTemporaryFile() as file:
parent = core.QObject()
path = Path(file.name)

w = core.FileWatcher(parent=parent, file=path)

# Modify the file to trigger the watcher
with qtbot.waitSignal(w.fileChanged):
with qtbot.waitSignal(w.fileSizeChanged) as blocker:
with open(path, 'a') as f:
f.write('Hello, World!')
assert blocker.args[0] == path.stat().st_size

# Modify the file (without changing its size)
with qtbot.waitSignal(w.fileChanged):
with qtbot.assertNotEmitted(w.fileSizeChanged, wait=100):
with open(path, 'w') as f:
f.write('Hello, World?')

with pytest.raises(FileNotFoundError):
core.FileWatcher(parent=parent, file='non-existent file')

0 comments on commit 678114c

Please sign in to comment.