[GSoC] Tour Creation Guide

• Jul 17, 2018 - 00:40

Hey there, I've been working on adding tours to MuseScore to help teach new users the functionality of MuseScore. Below is a guide on how the tours work and how to add your own. Since this is still in development, these things are subject to change. Check out this YouTube video to see some example tours. To keep track of development, check out my blog.

Adding Tours

A tour is a guide to help showcase or further explain a feature in MuseScore. The tour will display message boxes and highlight components to show off the feature. In order to add a tour, you need to know how the tour mechanics work.

Under the Hood

The tours are stored in a folder located in "share/tours/". When MuseScore is started, the tours are loaded into a list for easy lookup.

There are three ways to activate a tour--one tour can be activated multiple ways:

  1. Programatically: the tour starts when a specific point in the code is reached.
  2. Attaching: the tour is attached to an object by an event. When the event occurs, the tour begins.
  3. Using Shortcuts or Events: the tour is activated whenever the shortcut occurs or the event triggers.

Once the tour starts, it finds any widgets that were listed and creates a gray overlay around them. Next, it attempts to place the message box near the widgets in some way. It makes sure the message box does not go off the screen by "sliding" it back on until it is within the frame. Finally, it displays the message box.

The Steps

Adding a tour is a simple two (or one) step process. The first step is to create the XML for the tour. The second step is to add the tour to the code, if necessary.

Step 1: Create the XML

  • Go to the folder "share/tours/"
  • Add a file ending in .tour
  • Follow the following XML syntax: (Subject to change)
<?xml version="1.0" encoding="UTF-8"?>
<Tour name="tourName"> <!-- Used as lookup for starting tours -->
  <Event objectName="object-name">eventType</Event> <!-- (Optional) Trigger the tour when the object recieves a "QEvent::Type" event -->
  <Event objectName="object-name2">eventType</Event>
  <Shortcut>shortcut-name</Shortcut> <!-- (Optional) Trigger the tour on the shortcut -->
  <Message> <!-- Each message is a new msgbox in the tour -->
    <Text>Some message here</Text> <!-- Location of the main text -->
    <Widget>widget-name</Widget> <!-- (Optional) Widgets to highlight, searched via objectName -->
    <Text>Another message here</Text>

Step 2: Connect the Tour

(See "Under the Hood" for more explanation)

  • Programatically
    • Find the spot in the code that should activate the tour
    • Add TourHandler::startTour(tourName) listing the tour name that you put in the XML.
  • Attaching
    • Find the object you want to attach to.
    • Select the events you want to trigger the tour on.
    • Make sure you can access mscore
    • Add mscore->tourHandler()->attachTour(objectToAttachTo, event, tourName); listing the tour name that you put in the XML. This usually should be placed near the creation of the object.
      • Example: mscore->tourHanlder()->attachTour(w, QEvent::MouseButtonRelease, "note-input");
  • Using Shortcuts or Events
    • You're done! Nothing needs to be added if you are only using shortcuts or events.

Let me know if you have any ideas, suggestions, etc!


When MuseScore is started, the tours are loaded into a list for easy lookup
Do you mean the xml files are actually loaded and analysed at startup, like plugins are?
I am just a little worried about the time it would take to load them. On my laptop, it can take up to a minute to start MuseScore, and more than a minute and 15 seconds if I try to load too much plugins.
I already feel like it's too much (okay, I have a really tiny and old 32 bits computer) so loading a list of tours at startup worries me.
if tours are analysed at startup, could you calculate the time it takes to load a few of them at startup?
If it turns out it takes some time, is loading these tours at startup absolutely necessary?

Find the spot in the code that should activate the tour
Add TourHandler::startTour(tourName) listing the tour name that you put in the XML.

I feel like everything in tours should be written directly in the .tour file, for better versatility.
Could there be an 'attachTourToSignal(tour, &signal)' which would do a

 connect(..., signal, tourHandler, runTour(tourName)) <cpp>
an <cpp>attachTourToFunction(tour, &function) would also be great, but this may be harder to implement.
With those 2 functions, everything would be written in the .tour file.

In reply to by ecstrema

I have a hard time imagining the tours would add but a fraction of a second to load. When you say it takes a minute to startup, are you perhaps using some special soundfonts? Those are the things that usually take the longest.

But, noted - a way to disable loading of tours sounds like a potential enhancement.

In reply to by JoshuaBonn1

I think it would work the way you said, implementing the tour directly in the code, but I feel like it would be easier if everything could be done in the same place: the .tour file(s). It would prevent having to look all over the code to find when the startTour function for that .tour was called.
If the tour can be started at multiple places in the code, it could become quite nasty.
I follow the rule: "keep your eggs in the same basket and you won't lose any".

In reply to by ecstrema

I understand what you mean, but you didn't answer my question. Can you give me some example tours where attachTourToSignal and attachTourToFunction are necessary--i.e. there is no other existing way to start the tour and "startTour" would just be too messy? Just to clarify, you mean that in someway the XML will hold the data necessary to run either of those functions?

In reply to by ecstrema

To be clear, though - and Joshua can correct me if I mis-speak here:

You can do "everything" in the XML file, if you are willing to live within certain limitations. Basically, you can attach help to any widget with an object name or any command found in the list of commands you can assign shortcuts. You can also edit any tour that was already hooked up. All without touching the source code to MuseScore at all. Just knowing the object or command name is enough. That might not have been completely clear from the original writeup, but those last three bullet items are "either / or", not "first this, then...". So, step 2 is non-existent when attaching to these types of events.

The only time you'd need to touch the code is if you wanted to attach help to some other event. Like, for instance, if you wanted a tour that was executed the first time you tried entering a Unicode flat sign into a chord symbol (something that came up in another thread I just responded to), to say "please use the letter 'b' instead". Or if you tried editing the length of a volta by double-click and drag rather than shift+right arrow. These are things you'd have to program more specifically. Probably we could anticipate some of these things and add hooks to the code so that tours could be more easily written in the future.

In reply to by ericfontainejazz

note to future people: simply changing that CMakeLists.txt was not sufficient. I also had to rebuild entire project. But I think the sufficient necessary step is to delete the install directory and delete the build/tours directory, and then rebuild. That way the newly created tour file will actually get copied to the build and install tour directories.

In reply to by Jojo-Schmitz

Nope. I just tried it again...I modified share\tours\CMakeLists.txt with an additional .tour file and added an addition .tour file to that directory. Did build INSTALL again from inside msvc, and it didn't copy the new file over to destination msvc.install_x64/tours. Even if I build mscore first and then build INSTALL, doesn't work.

I have to re-running msvc_build.bat debug from powershell and then build install again for the file to be copied over. So it seems regular build INSTALL doesn't quite register the file change. I've tried this a few times...same behavior.

This could partially be because I am using debug configuration, not the default RelWithDebug.

I'm thinking it would be nice to have an option 'title' element for each Tour, which would display in the popup window so would be for instance "Tour: [tour title]" like "Tour: Spanner element range".

Do you still have an unanswered question? Please log in first to post your question.