New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Characters and words count #536

Closed
ghost opened this Issue Mar 6, 2017 · 127 comments

Comments

Projects
None yet
3 participants
@ghost

ghost commented Mar 6, 2017

Hi,

I mostly use your app for writing long texts.
Can you please add a way to see characters (space included) and words count ?

It will be very good if it is in real time in the status bar. Else, a popup menu to see the document statistic will do it ;)

Best regards.

++

@pbek pbek added the enhancement label Mar 6, 2017

@pbek

This comment has been minimized.

Owner

pbek commented Mar 6, 2017

You could write a custom action in a script that outputs the information to the log panel...
See: http://docs.qownnotes.org/en/develop/scripting/README.html#registering-a-custom-action

@ghost

This comment has been minimized.

ghost commented Mar 6, 2017

Yes, I saw that but I am not familiar wit Qt.
I will try.

Thanks anyway.

@pbek

This comment has been minimized.

Owner

pbek commented Mar 6, 2017

Scripts are written in JavaScript.
@Maboroshy is the master of all things scripted. 😉

You need to get the current note (see: http://docs.qownnotes.org/en/develop/scripting/README.html#getting-the-current-note) and get the noteText from it. Then you could count the characters and words of it.

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented Mar 7, 2017

Your summon looks like a challenge.
I would try using qtquick pop-up window for result instead of log output.

By the way, is it possible to have qtquick driven scriptable panel? That's a counter-challenge 😉

I don't need it personally, but it seems perfect for the task.

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented Mar 7, 2017

Here is the prototype:

import QtQml 2.0
import com.qownnotes.noteapi 1.0
import QtQuick 2.3
import QtQuick.Dialogs 1.2
    
MessageDialog {
    title: "Note statistics"
    
    function init() {
        script.registerCustomAction("statNote", "Show note statistics", "Stat");
    }
    
    function statNote(note) {
        return  "Characters: " + note.noteText.match(/[^ ]/gi).length + "\n" + 
                "Words: " + note.noteText.match(/[\w\d]+/gi).length + "\n" + 
                "Lines: " + note.noteText.split(/\r\n|\r|\n/).length
    }
    
    function customActionInvoked(action) {
        if (action == "statNote") {
            text = statNote(script.currentNote())
            visible = true
        }
    }
} 

Regexps still need some tuning.

@pbek

This comment has been minimized.

Owner

pbek commented Mar 8, 2017

@Maboroshy, great job! Did you manage to open a qtquick dialog?

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented Mar 8, 2017

I did. The script above fully works, just needs some polish.

@pbek

This comment has been minimized.

Owner

pbek commented Mar 8, 2017

It really works, you are a genius! Maybe you also want to set a nice freedesktop theme icon for the action and set the last parameter to true to show it in the context menu (if that makes sense).

I'm waiting for a pull request. ;)

Maybe you even want to document how to create dialogs in https://github.com/pbek/QOwnNotes/blob/develop/doc/scripting/README.rst. 👍

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented Mar 8, 2017

I'll do the pull request, but documenting is too much for me now. The whole object-oriented programming and specifically qtquick is very new for me.

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented Mar 8, 2017

But I got characters without spaces while @fred-g wanted them in. Making up obstacles for myself as usual. 😄

@pbek

This comment has been minimized.

Owner

pbek commented Mar 8, 2017

The whole object-oriented programming and specifically qtquick is very new for me.

Just a new paragraph on how to open a dialog, like you did in the script would be more than awesome. 🎉

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented Mar 8, 2017

It's not that easy. The dialog is generated when script is initialized. So to make it show some fresh data, like an always changing current note, you need to change it's properties, like text, before showing it. It works little differently for different types of dialogs. There are some more nuances that took me quite a lot of time to work out. So that's much more than a new paragraph. And for any real programmer, who worked with qtquick, it will look like a babytalk of guy who doesn't know what he's writing about.

@pbek

This comment has been minimized.

Owner

pbek commented Mar 8, 2017

You are the "most real" programmer I'm currently aware of who works with QOwnNotes. 😁

So to make it show some fresh data, like an always changing current note

Yes, for me the dialog shows the same numbers, when I modify the note and click the "Stats" button again.

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented Mar 8, 2017

That will need some more work...

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented Mar 8, 2017

I wonder, when the app updates note.noteText? Because, as far as I understand, the script counts data in note.noteText each time custom action invoked. It reacts to the change of current note, so it gets an actual note.noteText.

Looks like writing something to the note won't change note.noteText, even saving changes won't. Reloading note will, and the script will show correct stats.

@pbek pbek added this to the 17.03.1 milestone Mar 8, 2017

@pbek

This comment has been minimized.

Owner

pbek commented Mar 8, 2017

@Maboroshy, you are right! Will be fixed in version 17.03.1! Thank you for finding out.

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented Mar 8, 2017

It was actually you who found that out in previous post. 😄

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented Mar 8, 2017

I'll make a pull request after 17.03.1 release. Mostly final script:

import QtQml 2.0
import com.qownnotes.noteapi 1.0
import QtQuick 2.3
import QtQuick.Dialogs 1.2

/* This script will add a custom action with a toolbar button that will show 
 * a current note statistics in a pop-up window. */

MessageDialog {
    
    function init() {
        script.registerCustomAction("statNote", "Show note statistics", "Stats", "view-statistics.svg")
    }
    
    function customActionInvoked(action) {
        if (action == "statNote") {
            
            var note = script.currentNote()
            
            title = "Note statistics"
            text = "Characters (inc. spaces): " + note.noteText.length + "\n" +
                   "Characters (w/o spaces): " + note.noteText.match(/[^ ]/gi).length + "\n" + 
                   "Words: " + note.noteText.split(/\s+/).length + "\n" + 
                   "Paragraphs: " + (note.noteText.match(/^.*?\S/gm) || "").length
            visible = true
        }
    }
} 

Issue closed.

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented Mar 8, 2017

But a scriptable panel would be much cooler. 😉

@pbek

This comment has been minimized.

Owner

pbek commented Mar 8, 2017

If you find a way in QML to create a QDockWidget or to write to one I certainly will integrate one!

@ghost

This comment has been minimized.

ghost commented Mar 8, 2017

Wow, that is so interesting to read you, thank you !

@Maboroshy Yes, in Writing, characters count is spaces included ;)

@pbek

This comment has been minimized.

Owner

pbek commented Mar 8, 2017

17.03.1

  • the current note in the scripting function script.currentNote() will now
    be update correctly when when text is written to it by the user
@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented Mar 8, 2017

Ok then, here we go. I've made some research and found out that you can't just put QML content into QDockWidget. There's a bug in Qt bugtracker, but nobody cares. But...
I think it can be implemented as a special panel that will show a text data generated inside QML script. Mostly like a log panel, but with just last message visible.

@pbek

This comment has been minimized.

Owner

pbek commented Mar 8, 2017

There now is a new release, could you please test it and report if it works for you?

@pbek

This comment has been minimized.

Owner

pbek commented Mar 8, 2017

I think it can be implemented as a special panel that will show a text data generated inside QML script. Mostly like a log panel, but with just last message visible.

We need a way to create a QDockWidget or something... but I don't even know how to store / restore the positions of user generated panels...

@pbek

This comment has been minimized.

Owner

pbek commented Mar 8, 2017

@Maboroshy or can you also create a new dialog with user interface elements from a QML?

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented Mar 8, 2017

It won't be user generated.
You'll make a new log panel that will show last message sent by QML function. No history, no scrolling, one message.
I'll make QML that will use something like script.panel("my text") to make the output to that panel like we do with the log. I'll put note statistics as "my text" and invoke this command by onNoteStored hook.

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented Mar 8, 2017

The thing is, to make it right we will need a panel for each script that wants a panel. So it can be like with custom action buttons, with the init thing. Don't know if it's possible for a full scale panels.

@pbek

This comment has been minimized.

Owner

pbek commented May 20, 2017

There now is a new release, could you please test it and report if it works for you?

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented May 20, 2017

You do! I'll try pulling in nsx2md with these new goodies.

@pbek

This comment has been minimized.

Owner

pbek commented May 20, 2017

I'm looking forward to it! :) I don't even want to remember how many hours I worked this week to get that whole scripting thing going... (and how many times I rewrote certain parts of it this week) :)

@pbek

This comment has been minimized.

Owner

pbek commented May 22, 2017

17.05.9

  • added information about enabled scripts to the Debug settings
  • now information about the currently installed version of the script will be
    shown in the Script repository if the script is already installed
  • you can now also reinstall or update your scripts in the Script repository
  • the user interface of the Scripting settings was adapted to make the
    entering of script settings more clear
  • fixed a problem with automatically setting the name of a local script in
    the Scripting settings

There now is a new release, could you please test it and report if it works for you?

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented May 22, 2017

New version works for me. Script settings look nicer now.

Maybe the path to script should stay with the script name for local scripts. It's going down when settings appear.

Also maybe selecting directories should be enabled for QFileDialog, or maybe there should be "directory" setting type.

But I'm still not close to using it. I'm on a rampage to make nsx2md to import note tags directly to db.

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented May 22, 2017

Script settings tend to blend and multiply when going from one script to another.
I've imported "md to bbcode" script which has a "pandoc" setting. Selected it first time - one setting, which is ok. Selected second time - two pandoc settings, which is not ok, etc.

@pbek

This comment has been minimized.

Owner

pbek commented May 22, 2017

@Maboroshy, damn, I didn't took care about removing those widgets before setting them again and I didn't even notice it while testing... Thank you for mentioning!

@pbek

This comment has been minimized.

Owner

pbek commented May 23, 2017

17.05.10

  • fixed duplication of script settings variables in the in the Scripting settings
  • moved the qml path settings above the script variable settings in the
    Scripting settings
@pbek

This comment has been minimized.

Owner

pbek commented May 23, 2017

There now is a new release, could you please test it and report if it works for you?

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented May 23, 2017

Duplication issue is resolved, and script path don't jump up and down anymore, which is a good thing.

@pbek

This comment has been minimized.

Owner

pbek commented May 23, 2017

Great, thank you for testing!

@pbek

This comment has been minimized.

Owner

pbek commented May 24, 2017

@Maboroshy we need more scripts from you in the repository! 😃 Your note stats script is so far the number one installed script! 👏 🎉

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented May 24, 2017

I'd say I'm working on it, but that is not true. Truth is I'm trying hard to find some time to work on it. But I will prevail!

@pbek

This comment has been minimized.

Owner

pbek commented May 24, 2017

You will prevail, my friend!

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented May 26, 2017

For some reason "integer" type setting can't be higher than 99.

@pbek

This comment has been minimized.

Owner

pbek commented May 26, 2017

might be a stupid default setting of the number control, I will take a look at it

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented May 26, 2017

I also get weird bug when shown settings may become out of sync with real settings. It's very hard to find out why it happens. May be I miss something obvious.

I wanted to make the note stats script customizable. Here's the test version:

import QtQml 2.0
import QOwnNotesTypes 1.0

Script {
    
    property string charSpaceName
    property string charSpaceLeftName
    property string charSpaceLeftTarget
    property string charNoSpaceName
    property string charNoSpaceLeftName
    property string charNoSpaceLeftTarget
    property string wordsName
    property string wordsLeftName
    property string wordsLeftTarget
    property string paragraphsName
    property string paragraphsLeftName
    property string paragraphsLeftTarget
    
    property variant settingsVariables: [
        {
            "identifier": "charSpaceName",
            "name": "Characters including spaces counter name",
            "description": "Put a counter name here or leave empty to disable",
            "type": "string",
            "default": "Chars",
        },
        {
            "identifier": "charSpaceLeftName",
            "name": "Characters including spaces left until target counter name",
            "description": "Put a counter name here or leave empty to disable",
            "type": "string",
            "default": "Chars left",
        },
        {
            "identifier": "charSpaceLeftTarget",
            "name": "Target number of characters including spaces",
            "description": "Put number of characters including spaces to count down to",
            "type": "integer",
            "default": "0",
        },
        {
            "identifier": "charNoSpaceName",
            "name": "Characters without spaces counter name",
            "description": "Put a counter name here or leave empty to disable",
            "type": "string",
            "default": "",
        },
        {
            "identifier": "charNoSpaceLeftName",
            "name": "Characters without spaces left until target counter name",
            "description": "Put a counter name here or leave empty to disable",
            "type": "string",
            "default": "",
        },
        {
            "identifier": "charNoSpaceLeftTarget",
            "name": "Target number of characters without spaces",
            "description": "Put number of characters without spaces to count down to",
            "type": "integer",
            "default": "0",
        },
        {
            "identifier": "wordsName",
            "name": "Words counter name",
            "description": "Put a counter name here or leave empty to disable",
            "type": "string",
            "default": "Words",
        },
        {
            "identifier": "wordsLeftName",
            "name": "Words left until target counter name",
            "description": "Put a counter name here or leave empty to disable",
            "type": "string",
            "default": "Words left",
        },
        {
            "identifier": "wordsLeftTarget",
            "name": "Target number of words",
            "description": "Put number of words to count down to",
            "type": "integer",
            "default": "0",
        },
        {
            "identifier": "paragraphsName",
            "name": "Paragraphs counter name",
            "description": "Put a counter name here or leave empty to disable",
            "type": "string",
            "default": "",
        },
        {
            "identifier": "paragraphsLeftName",
            "name": "Paragraphs left until target counter name",
            "description": "Put a counter name here or leave empty to disable",
            "type": "string",
            "default": "",
        },
        {
            "identifier": "paragraphsLeftTarget",
            "name": "Target number of paragraphs",
            "description": "Put number of paragraphs to count down to",
            "type": "integer",
            "default": "0",
        }
    ]
    
    function init() {script.registerLabel("note stats")}
    
    function noteStats(note) {
        
        var entry = "<td align=center>%1 <b>%2</b></th>\n"
        var entryLeft = "<td align=center>%1 <b>%2 / %3</b></th>\n"
        
        if (charSpaceName !== "") {
            var charSpace = entry.arg(charSpaceName)
                                 .arg(note.noteText.length)
        }
        else {var charSpace = ""}
        
        if (charSpaceLeftName !== "") {
            var charSpaceLeft = entryLeft.arg(charSpaceLeftName)
                                         .arg(charSpaceLeftTarget - note.noteText.length)
                                         .arg(charSpaceLeftTarget)
        }
        else {var charSpaceLeft = ""}
        
        if (charNoSpaceName !== "") {
            var charNoSpace = entry.arg(charNoSpaceName)
                                   .arg(note.noteText.match(/[^ ]/gi).length)
        }
        else {var charNoSpace = ""}
        
        if (charNoSpaceLeftName !== "") {
            var charNoSpaceLeft = entryLeft.arg(charNoSpaceLeftName)
                                           .arg(charNoSpaceLeftTarget - note.noteText.match(/[^ ]/gi).length)
                                           .arg(charNoSpaceLeftTarget)
        }
        else {var charNoSpaceLeft = ""}
        
        if (wordsName !== "") {
            var words = entry.arg(wordsName).arg(note.noteText.split(/\s+/).length)
        }
        else {var words = ""}
        
        if (wordsLeftName !== "") {
            var wordsLeft = entryLeft.arg(wordsLeftName)
                                     .arg(wordsLeftTarget - note.noteText.split(/\s+/).length)
                                     .arg(wordsLeftTarget)
        }
        else {var wordsLeft = ""}
        
        if (paragraphsName !== "") {
            var paragraphs = entry.arg(paragraphsName)
                                  .arg((note.noteText.match(/^.*?\S/gm) || "").length)
        }
        else {var paragraphs = ""}   
        
        if (paragraphsLeftName !== "") {
            var paragraphsLeft = entryLeft.arg(paragraphsLeftName)
                                          .arg(paragraphsLeftTarget - (note.noteText.match(/^.*?\S/gm) || "").length)
                                          .arg(paragraphsLeftTarget)
        }
        else {var paragraphsLeft = ""} 
        
        script.setLabelText("note stats", "<table align=center width=90%>\n<tr>\n" + 
        charSpace + charSpaceLeft + charNoSpace + charNoSpaceLeft + words + wordsLeft + paragraphs + paragraphsLeft + 
        "</tr>\n</table>")
    }
    
    function noteOpenedHook(note) {noteStats(note)}
    function onNoteStored(note) {noteStats(note)}
} 

When I change the first integer setting - the "Target number of characters including spaces", the "number of characters" indicator is gone, while being on in settings. And they are not any way connected in the script. Changing settings on and on I get more such weird bugs.

@pbek

This comment has been minimized.

Owner

pbek commented May 26, 2017

Do you overwrite one of the settings properties in your script?
And you seemed to have defined all settings properties as string, but configured some of them as integer in settingsVariables. Does that cause any troubles?

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented May 27, 2017

I don't overwrite anything, only make some "if" checks.

Having integer setting as QML strings doesn't cause any trouble. If I remember correctly, you change them to integer in C++ anyway. I've corrected it, but the issue is not resolved. Maybe that weird connection between settings vars, which is not in the script, changed after that.

Where are the in-script settings stored?

@pbek

This comment has been minimized.

Owner

pbek commented May 27, 2017

In-script settings are stored in the app-wide sqlite database (along with the script entries).

17.05.11

  • the full integer range of values can now be selected in the number selector
    in script variables in the Scripting settings
@pbek

This comment has been minimized.

Owner

pbek commented May 27, 2017

There now is a new release, could you please test it and report if it works for you?

@pbek

This comment has been minimized.

Owner

pbek commented May 29, 2017

@Maboroshy does the integer field work for you?

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented May 29, 2017

They do, thanks. I'm yet to instigate what's wrong with my script settings.

@pbek

This comment has been minimized.

Owner

pbek commented May 30, 2017

Thank you for testing!

@pbek

This comment has been minimized.

Owner

pbek commented Jun 10, 2017

@Maboroshy, I'm working on your dream of custom script dock widgets that can make full use of QtQuick, so that a script can has it's own QtQuick dock widget with it's own user interface! Quite tricky to implement...

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented Jun 10, 2017

You're always a step ahead of me. I have not yet uploaded any script with new settings feature.
Have you decided to bundle QtQuick with the app?

@pbek

This comment has been minimized.

Owner

pbek commented Jun 10, 2017

@pbek

This comment has been minimized.

Owner

pbek commented Jun 10, 2017

@Maboroshy, do you have any thoughts about QtQuick?

@Maboroshy

This comment has been minimized.

Contributor

Maboroshy commented Jun 10, 2017

About using it?

It was quite obvious before you've implemented script settings, "labels" and some dialogues. Because back then user could send something to a script only by custom action buttons and mostly could get nothing out. It's not that obvious now.

For example lets take an "attachments pane" script. For now we can easily implement a label that will show all the note's attachments, maybe even links to open them straight away from the pane. That's a core functionality in place.
Now we bring QtQuick in. The pane becomes not a rusty text string but a UI where every attachment is a button, each have a delete button that will delete link and file after a confirmation. But that's a 3 times more work to implement.

QtQuick will really shine in a very complex scripts, like a reminder or a task manager. But I don't think we will see many of those in repository soon. The amount of work for them is close to making a stand-alone app.

@pbek

This comment has been minimized.

Owner

pbek commented Jun 10, 2017

The amount of work for them is close to making a stand-alone app.

yes and no. yes, it still but be work but a new app would definitely be a multitude more work (with all its foundation that needs to be laid out) :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment