Showing a short statistic after Picard finishes

Is it possible to include the following statistics window after Picard has finished?

s

UPDATE

It would be good if Picard remembered the previous result (saved in some little file) and gave the current one next to him.

s

Iā€™m going to write a plugin that will output the above image.

I just need variables that are assigned to these icons below (in the right panel of Picard).

That would speed up writing the plugin.

I think I can handle the rest of the code, the ā€œforā€ and ā€œifā€ statements myself. :wink:

I used Total Commander to search for the words ā€œmodifiedā€ in the Picard source files.

He found 11 *.py files with this word.

Is this a good lead?

Is this variable in the itemviews.py file?

I need help with Python code.

I want to include the code for the ā€˜Remove Perfect Albumsā€™ plugin to include the ā€˜Calculatorā€™ popup code somewhere in the place marked by my comment.

PLUGIN_NAME = 'Remove Perfect Albums'
PLUGIN_AUTHOR = 'ichneumon, hrglgrmpf'
PLUGIN_DESCRIPTION = '''Remove all perfectly matched albums from the selection.'''
PLUGIN_VERSION = '0.3'
PLUGIN_API_VERSIONS = ['2.0', '2.1', '2.2', '2.3']
PLUGIN_LICENSE = "GPL-2.0"
PLUGIN_LICENSE_URL = "https://www.gnu.org/licenses/gpl-2.0.html"

from PyQt5.QtCore import QCoreApplication

from picard.album import Album
from picard.ui.itemviews import BaseAction, register_album_action


class RemovePerfectAlbums(BaseAction):
    NAME = 'Remove perfect albums'

    def callback(self, objs):
        for album in objs:
            if (isinstance(album, Album) and album.loaded
               and album.is_complete() and album.get_num_unsaved_files() == 0):
                self.tagger.remove_album(album)    
            QCoreApplication.processEvents()
            ##### SOMEWHERE HERE

register_album_action(RemovePerfectAlbums())

Kalkulator:

kalkulator01

#!/usr/bin/python3
# -*- coding: utf-8 -*-

from PyQt5.QtWidgets import QApplication, QWidget


class Kalkulator(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.interfejs()

    def interfejs(self):

        self.resize(300, 100)
        self.setWindowTitle("Prosty kalkulator")
        self.show()


if __name__ == '__main__':
    import sys

    app = QApplication(sys.argv)
    okno = Kalkulator()
    sys.exit(app.exec_())
1 Like

I will include any help in the PLUGIN_AUTHOR field.

This is a bit tricky, as Picard does not offer an explicit plugin hook to run something on exit.

However, as everything is Python and plugins have full access to all of Picard you can do essentially anything. Itā€™s just that none of the existing plugins explicitly runs on exit.

So first you want to have something that runs on exit. There is the (currently undocumented) ui_init extension point. You can use it to get access to run something on start and get access to an instance of picard.Tagger (see picard.tagger.py). This allows you to register a function to run on exit:

from picard.ui.mainwindow import register_ui_init
from PyQt5.QtCore import QCoreApplication

def ui_init(mainwindow):
    # Get the main application instance, an instance of picard.Tagger.
    tagger = QCoreApplication.instance()
    # Register a function to run on exit
    tagger.register_cleanup(on_exit)

def on_exit():
    tagger = QCoreApplication.instance()
    albums = tagger.albums
    # Iterate over the albums, check album state with album.is_complete()
    # and album.is_modified()
    # Then show a QDialog with the results (your code above was on the right track)

register_ui_init(ui_init)

The albums are of type picard.Album, see picard/album.py. Check this class for more details. The code you found in itemviews.py is the correct code that decides the icons. It uses Album.is_complete() and album.is_modified() to decide on the proper icon.

I havenā€™t tested the above code, but this should get you started.

2 Likes

Just a few notes: Because the plugin registers the exit callback with ui_init this means the plugin will need to be installed and active when Picard starts. Which means if this gets installed it needs a restart before the exit callback will work.

Also if Picard gets started with the plugin enabled the exit hook will run, no matter whether the user disables or uninstalls the plugin.

2 Likes

A bit difficult, but Iā€™m not going to ask you 10 questions a day. :wink:

Maybe once a week.

@outsidecontext

Only one thing today. :wink:

I added your lines and mine to the code:

A=A+1 perfect albums counter

print(A)

class RemovePerfectAlbums(BaseAction):
    NAME = 'Stat'

    def callback(self, objs):
        for album in objs:
            if (isinstance(album, Album) and album.loaded
               and album.is_complete() and album.get_num_unsaved_files() == 0):
                self.tagger.remove_album(album)
                A=A+1
            QCoreApplication.processEvents()
            print(A)

However, when I run the plugin, Picard shuts down.

I just want to check if the counting and quick display works.

Iā€™m new to Python, but I know Pascal and C++.

Thatā€™s likely because some part of the code is raising an exception.

Try running picard (or picard.exe on Windows) from a command prompt. It should show you more information why it crashed. For any development it is always best to run Picard that way.

2 Likes

@outsidecontext

It didnā€™t show any errors.

Yes, on Windows there are a few ways to crash that unfortunately donā€™t show up anywhere. If you share the full plugin as you currently have it Iā€™ll take a look and try to run it here.

1 Like
PLUGIN_NAME = 'Stat'
PLUGIN_AUTHOR = 'ichneumon, hrglgrmpf'
PLUGIN_DESCRIPTION = '''Remove all perfectly matched albums from the selection.'''
PLUGIN_VERSION = '0.3'
PLUGIN_API_VERSIONS = ['2.0', '2.1', '2.2', '2.3']
PLUGIN_LICENSE = "GPL-2.0"
PLUGIN_LICENSE_URL = "https://www.gnu.org/licenses/gpl-2.0.html"

from PyQt5.QtCore import QCoreApplication

from picard.album import Album
from picard.ui.itemviews import BaseAction, register_album_action
from picard.ui.mainwindow import register_ui_init

def ui_init(mainwindow):
    # Get the main application instance, an instance of picard.Tagger.
    tagger = QCoreApplication.instance()
    # Register a function to run on exit
    tagger.register_cleanup(on_exit)

def on_exit():
    tagger = QCoreApplication.instance()
    albums = tagger.albums
    # Iterate over the albums, check album state with album.is_complete()
    # and album.is_modified()
    # Then show a QDialog with the results (your code above was on the right track)

class RemovePerfectAlbums(BaseAction):
    NAME = 'Stat'

    def callback(self, objs):
        for album in objs:
            if (isinstance(album, Album) and album.loaded
               and album.is_complete() and album.get_num_unsaved_files() == 0):
                self.tagger.remove_album(album)
                A=A+1
            QCoreApplication.processEvents()
            print(A)

register_album_action(RemovePerfectAlbums())
register_ui_init(ui_init)

'''
from picard.ui.mainwindow import register_ui_init
from PyQt5.QtCore import QCoreApplication

def ui_init(mainwindow):
    # Get the main application instance, an instance of picard.Tagger.
    tagger = QCoreApplication.instance()
    # Register a function to run on exit
    tagger.register_cleanup(on_exit)

def on_exit():
    tagger = QCoreApplication.instance()
    albums = tagger.albums
    # Iterate over the albums, check album state with album.is_complete()
    # and album.is_modified()
    # Then show a QDialog with the results (your code above was on the right track)

register_ui_init(ui_init)

'''

The problem is that on in the callback the variable A was not initialized.

Do something like this:

    A = 1
    def callback(self, objs):
        for album in objs:
            ...

That there was no output at all when running from command prompt likely was because Picard was already running. If it is then by default picard just passes arguments to the running instance. So either make sure Picard is stopped or run picard.exe --stand-alone-instance (or short picard.exe -s) to explicitly launch a new instance.

1 Like

Additional question and itā€™s not a problem.

Do I need to install a new version of the plugin or just copy the ZIP and reload the plugin list?

Only thing needed is to put the plugin code into a file with a unique name (e.g. stat.py) into the plugin directory. Then restart Picard. The plugin will show up in the plugin list where you can enable it.

1 Like

I know all this. :wink:

I can turn on plug-ins.

I wanted a faster method (less clicking) to update the plugin.

Still no errors