@ -0,0 +1 @@ |
|||
template/gitignore.template |
@ -0,0 +1,2 @@ |
|||
2019-04-15 Version 2.0 |
|||
Initial Release |
@ -0,0 +1,674 @@ |
|||
GNU GENERAL PUBLIC LICENSE |
|||
Version 3, 29 June 2007 |
|||
|
|||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> |
|||
Everyone is permitted to copy and distribute verbatim copies |
|||
of this license document, but changing it is not allowed. |
|||
|
|||
Preamble |
|||
|
|||
The GNU General Public License is a free, copyleft license for |
|||
software and other kinds of works. |
|||
|
|||
The licenses for most software and other practical works are designed |
|||
to take away your freedom to share and change the works. By contrast, |
|||
the GNU General Public License is intended to guarantee your freedom to |
|||
share and change all versions of a program--to make sure it remains free |
|||
software for all its users. We, the Free Software Foundation, use the |
|||
GNU General Public License for most of our software; it applies also to |
|||
any other work released this way by its authors. You can apply it to |
|||
your programs, too. |
|||
|
|||
When we speak of free software, we are referring to freedom, not |
|||
price. Our General Public Licenses are designed to make sure that you |
|||
have the freedom to distribute copies of free software (and charge for |
|||
them if you wish), that you receive source code or can get it if you |
|||
want it, that you can change the software or use pieces of it in new |
|||
free programs, and that you know you can do these things. |
|||
|
|||
To protect your rights, we need to prevent others from denying you |
|||
these rights or asking you to surrender the rights. Therefore, you have |
|||
certain responsibilities if you distribute copies of the software, or if |
|||
you modify it: responsibilities to respect the freedom of others. |
|||
|
|||
For example, if you distribute copies of such a program, whether |
|||
gratis or for a fee, you must pass on to the recipients the same |
|||
freedoms that you received. You must make sure that they, too, receive |
|||
or can get the source code. And you must show them these terms so they |
|||
know their rights. |
|||
|
|||
Developers that use the GNU GPL protect your rights with two steps: |
|||
(1) assert copyright on the software, and (2) offer you this License |
|||
giving you legal permission to copy, distribute and/or modify it. |
|||
|
|||
For the developers' and authors' protection, the GPL clearly explains |
|||
that there is no warranty for this free software. For both users' and |
|||
authors' sake, the GPL requires that modified versions be marked as |
|||
changed, so that their problems will not be attributed erroneously to |
|||
authors of previous versions. |
|||
|
|||
Some devices are designed to deny users access to install or run |
|||
modified versions of the software inside them, although the manufacturer |
|||
can do so. This is fundamentally incompatible with the aim of |
|||
protecting users' freedom to change the software. The systematic |
|||
pattern of such abuse occurs in the area of products for individuals to |
|||
use, which is precisely where it is most unacceptable. Therefore, we |
|||
have designed this version of the GPL to prohibit the practice for those |
|||
products. If such problems arise substantially in other domains, we |
|||
stand ready to extend this provision to those domains in future versions |
|||
of the GPL, as needed to protect the freedom of users. |
|||
|
|||
Finally, every program is threatened constantly by software patents. |
|||
States should not allow patents to restrict development and use of |
|||
software on general-purpose computers, but in those that do, we wish to |
|||
avoid the special danger that patents applied to a free program could |
|||
make it effectively proprietary. To prevent this, the GPL assures that |
|||
patents cannot be used to render the program non-free. |
|||
|
|||
The precise terms and conditions for copying, distribution and |
|||
modification follow. |
|||
|
|||
TERMS AND CONDITIONS |
|||
|
|||
0. Definitions. |
|||
|
|||
"This License" refers to version 3 of the GNU General Public License. |
|||
|
|||
"Copyright" also means copyright-like laws that apply to other kinds of |
|||
works, such as semiconductor masks. |
|||
|
|||
"The Program" refers to any copyrightable work licensed under this |
|||
License. Each licensee is addressed as "you". "Licensees" and |
|||
"recipients" may be individuals or organizations. |
|||
|
|||
To "modify" a work means to copy from or adapt all or part of the work |
|||
in a fashion requiring copyright permission, other than the making of an |
|||
exact copy. The resulting work is called a "modified version" of the |
|||
earlier work or a work "based on" the earlier work. |
|||
|
|||
A "covered work" means either the unmodified Program or a work based |
|||
on the Program. |
|||
|
|||
To "propagate" a work means to do anything with it that, without |
|||
permission, would make you directly or secondarily liable for |
|||
infringement under applicable copyright law, except executing it on a |
|||
computer or modifying a private copy. Propagation includes copying, |
|||
distribution (with or without modification), making available to the |
|||
public, and in some countries other activities as well. |
|||
|
|||
To "convey" a work means any kind of propagation that enables other |
|||
parties to make or receive copies. Mere interaction with a user through |
|||
a computer network, with no transfer of a copy, is not conveying. |
|||
|
|||
An interactive user interface displays "Appropriate Legal Notices" |
|||
to the extent that it includes a convenient and prominently visible |
|||
feature that (1) displays an appropriate copyright notice, and (2) |
|||
tells the user that there is no warranty for the work (except to the |
|||
extent that warranties are provided), that licensees may convey the |
|||
work under this License, and how to view a copy of this License. If |
|||
the interface presents a list of user commands or options, such as a |
|||
menu, a prominent item in the list meets this criterion. |
|||
|
|||
1. Source Code. |
|||
|
|||
The "source code" for a work means the preferred form of the work |
|||
for making modifications to it. "Object code" means any non-source |
|||
form of a work. |
|||
|
|||
A "Standard Interface" means an interface that either is an official |
|||
standard defined by a recognized standards body, or, in the case of |
|||
interfaces specified for a particular programming language, one that |
|||
is widely used among developers working in that language. |
|||
|
|||
The "System Libraries" of an executable work include anything, other |
|||
than the work as a whole, that (a) is included in the normal form of |
|||
packaging a Major Component, but which is not part of that Major |
|||
Component, and (b) serves only to enable use of the work with that |
|||
Major Component, or to implement a Standard Interface for which an |
|||
implementation is available to the public in source code form. A |
|||
"Major Component", in this context, means a major essential component |
|||
(kernel, window system, and so on) of the specific operating system |
|||
(if any) on which the executable work runs, or a compiler used to |
|||
produce the work, or an object code interpreter used to run it. |
|||
|
|||
The "Corresponding Source" for a work in object code form means all |
|||
the source code needed to generate, install, and (for an executable |
|||
work) run the object code and to modify the work, including scripts to |
|||
control those activities. However, it does not include the work's |
|||
System Libraries, or general-purpose tools or generally available free |
|||
programs which are used unmodified in performing those activities but |
|||
which are not part of the work. For example, Corresponding Source |
|||
includes interface definition files associated with source files for |
|||
the work, and the source code for shared libraries and dynamically |
|||
linked subprograms that the work is specifically designed to require, |
|||
such as by intimate data communication or control flow between those |
|||
subprograms and other parts of the work. |
|||
|
|||
The Corresponding Source need not include anything that users |
|||
can regenerate automatically from other parts of the Corresponding |
|||
Source. |
|||
|
|||
The Corresponding Source for a work in source code form is that |
|||
same work. |
|||
|
|||
2. Basic Permissions. |
|||
|
|||
All rights granted under this License are granted for the term of |
|||
copyright on the Program, and are irrevocable provided the stated |
|||
conditions are met. This License explicitly affirms your unlimited |
|||
permission to run the unmodified Program. The output from running a |
|||
covered work is covered by this License only if the output, given its |
|||
content, constitutes a covered work. This License acknowledges your |
|||
rights of fair use or other equivalent, as provided by copyright law. |
|||
|
|||
You may make, run and propagate covered works that you do not |
|||
convey, without conditions so long as your license otherwise remains |
|||
in force. You may convey covered works to others for the sole purpose |
|||
of having them make modifications exclusively for you, or provide you |
|||
with facilities for running those works, provided that you comply with |
|||
the terms of this License in conveying all material for which you do |
|||
not control copyright. Those thus making or running the covered works |
|||
for you must do so exclusively on your behalf, under your direction |
|||
and control, on terms that prohibit them from making any copies of |
|||
your copyrighted material outside their relationship with you. |
|||
|
|||
Conveying under any other circumstances is permitted solely under |
|||
the conditions stated below. Sublicensing is not allowed; section 10 |
|||
makes it unnecessary. |
|||
|
|||
3. Protecting Users' Legal Rights From Anti-Circumvention Law. |
|||
|
|||
No covered work shall be deemed part of an effective technological |
|||
measure under any applicable law fulfilling obligations under article |
|||
11 of the WIPO copyright treaty adopted on 20 December 1996, or |
|||
similar laws prohibiting or restricting circumvention of such |
|||
measures. |
|||
|
|||
When you convey a covered work, you waive any legal power to forbid |
|||
circumvention of technological measures to the extent such circumvention |
|||
is effected by exercising rights under this License with respect to |
|||
the covered work, and you disclaim any intention to limit operation or |
|||
modification of the work as a means of enforcing, against the work's |
|||
users, your or third parties' legal rights to forbid circumvention of |
|||
technological measures. |
|||
|
|||
4. Conveying Verbatim Copies. |
|||
|
|||
You may convey verbatim copies of the Program's source code as you |
|||
receive it, in any medium, provided that you conspicuously and |
|||
appropriately publish on each copy an appropriate copyright notice; |
|||
keep intact all notices stating that this License and any |
|||
non-permissive terms added in accord with section 7 apply to the code; |
|||
keep intact all notices of the absence of any warranty; and give all |
|||
recipients a copy of this License along with the Program. |
|||
|
|||
You may charge any price or no price for each copy that you convey, |
|||
and you may offer support or warranty protection for a fee. |
|||
|
|||
5. Conveying Modified Source Versions. |
|||
|
|||
You may convey a work based on the Program, or the modifications to |
|||
produce it from the Program, in the form of source code under the |
|||
terms of section 4, provided that you also meet all of these conditions: |
|||
|
|||
a) The work must carry prominent notices stating that you modified |
|||
it, and giving a relevant date. |
|||
|
|||
b) The work must carry prominent notices stating that it is |
|||
released under this License and any conditions added under section |
|||
7. This requirement modifies the requirement in section 4 to |
|||
"keep intact all notices". |
|||
|
|||
c) You must license the entire work, as a whole, under this |
|||
License to anyone who comes into possession of a copy. This |
|||
License will therefore apply, along with any applicable section 7 |
|||
additional terms, to the whole of the work, and all its parts, |
|||
regardless of how they are packaged. This License gives no |
|||
permission to license the work in any other way, but it does not |
|||
invalidate such permission if you have separately received it. |
|||
|
|||
d) If the work has interactive user interfaces, each must display |
|||
Appropriate Legal Notices; however, if the Program has interactive |
|||
interfaces that do not display Appropriate Legal Notices, your |
|||
work need not make them do so. |
|||
|
|||
A compilation of a covered work with other separate and independent |
|||
works, which are not by their nature extensions of the covered work, |
|||
and which are not combined with it such as to form a larger program, |
|||
in or on a volume of a storage or distribution medium, is called an |
|||
"aggregate" if the compilation and its resulting copyright are not |
|||
used to limit the access or legal rights of the compilation's users |
|||
beyond what the individual works permit. Inclusion of a covered work |
|||
in an aggregate does not cause this License to apply to the other |
|||
parts of the aggregate. |
|||
|
|||
6. Conveying Non-Source Forms. |
|||
|
|||
You may convey a covered work in object code form under the terms |
|||
of sections 4 and 5, provided that you also convey the |
|||
machine-readable Corresponding Source under the terms of this License, |
|||
in one of these ways: |
|||
|
|||
a) Convey the object code in, or embodied in, a physical product |
|||
(including a physical distribution medium), accompanied by the |
|||
Corresponding Source fixed on a durable physical medium |
|||
customarily used for software interchange. |
|||
|
|||
b) Convey the object code in, or embodied in, a physical product |
|||
(including a physical distribution medium), accompanied by a |
|||
written offer, valid for at least three years and valid for as |
|||
long as you offer spare parts or customer support for that product |
|||
model, to give anyone who possesses the object code either (1) a |
|||
copy of the Corresponding Source for all the software in the |
|||
product that is covered by this License, on a durable physical |
|||
medium customarily used for software interchange, for a price no |
|||
more than your reasonable cost of physically performing this |
|||
conveying of source, or (2) access to copy the |
|||
Corresponding Source from a network server at no charge. |
|||
|
|||
c) Convey individual copies of the object code with a copy of the |
|||
written offer to provide the Corresponding Source. This |
|||
alternative is allowed only occasionally and noncommercially, and |
|||
only if you received the object code with such an offer, in accord |
|||
with subsection 6b. |
|||
|
|||
d) Convey the object code by offering access from a designated |
|||
place (gratis or for a charge), and offer equivalent access to the |
|||
Corresponding Source in the same way through the same place at no |
|||
further charge. You need not require recipients to copy the |
|||
Corresponding Source along with the object code. If the place to |
|||
copy the object code is a network server, the Corresponding Source |
|||
may be on a different server (operated by you or a third party) |
|||
that supports equivalent copying facilities, provided you maintain |
|||
clear directions next to the object code saying where to find the |
|||
Corresponding Source. Regardless of what server hosts the |
|||
Corresponding Source, you remain obligated to ensure that it is |
|||
available for as long as needed to satisfy these requirements. |
|||
|
|||
e) Convey the object code using peer-to-peer transmission, provided |
|||
you inform other peers where the object code and Corresponding |
|||
Source of the work are being offered to the general public at no |
|||
charge under subsection 6d. |
|||
|
|||
A separable portion of the object code, whose source code is excluded |
|||
from the Corresponding Source as a System Library, need not be |
|||
included in conveying the object code work. |
|||
|
|||
A "User Product" is either (1) a "consumer product", which means any |
|||
tangible personal property which is normally used for personal, family, |
|||
or household purposes, or (2) anything designed or sold for incorporation |
|||
into a dwelling. In determining whether a product is a consumer product, |
|||
doubtful cases shall be resolved in favor of coverage. For a particular |
|||
product received by a particular user, "normally used" refers to a |
|||
typical or common use of that class of product, regardless of the status |
|||
of the particular user or of the way in which the particular user |
|||
actually uses, or expects or is expected to use, the product. A product |
|||
is a consumer product regardless of whether the product has substantial |
|||
commercial, industrial or non-consumer uses, unless such uses represent |
|||
the only significant mode of use of the product. |
|||
|
|||
"Installation Information" for a User Product means any methods, |
|||
procedures, authorization keys, or other information required to install |
|||
and execute modified versions of a covered work in that User Product from |
|||
a modified version of its Corresponding Source. The information must |
|||
suffice to ensure that the continued functioning of the modified object |
|||
code is in no case prevented or interfered with solely because |
|||
modification has been made. |
|||
|
|||
If you convey an object code work under this section in, or with, or |
|||
specifically for use in, a User Product, and the conveying occurs as |
|||
part of a transaction in which the right of possession and use of the |
|||
User Product is transferred to the recipient in perpetuity or for a |
|||
fixed term (regardless of how the transaction is characterized), the |
|||
Corresponding Source conveyed under this section must be accompanied |
|||
by the Installation Information. But this requirement does not apply |
|||
if neither you nor any third party retains the ability to install |
|||
modified object code on the User Product (for example, the work has |
|||
been installed in ROM). |
|||
|
|||
The requirement to provide Installation Information does not include a |
|||
requirement to continue to provide support service, warranty, or updates |
|||
for a work that has been modified or installed by the recipient, or for |
|||
the User Product in which it has been modified or installed. Access to a |
|||
network may be denied when the modification itself materially and |
|||
adversely affects the operation of the network or violates the rules and |
|||
protocols for communication across the network. |
|||
|
|||
Corresponding Source conveyed, and Installation Information provided, |
|||
in accord with this section must be in a format that is publicly |
|||
documented (and with an implementation available to the public in |
|||
source code form), and must require no special password or key for |
|||
unpacking, reading or copying. |
|||
|
|||
7. Additional Terms. |
|||
|
|||
"Additional permissions" are terms that supplement the terms of this |
|||
License by making exceptions from one or more of its conditions. |
|||
Additional permissions that are applicable to the entire Program shall |
|||
be treated as though they were included in this License, to the extent |
|||
that they are valid under applicable law. If additional permissions |
|||
apply only to part of the Program, that part may be used separately |
|||
under those permissions, but the entire Program remains governed by |
|||
this License without regard to the additional permissions. |
|||
|
|||
When you convey a copy of a covered work, you may at your option |
|||
remove any additional permissions from that copy, or from any part of |
|||
it. (Additional permissions may be written to require their own |
|||
removal in certain cases when you modify the work.) You may place |
|||
additional permissions on material, added by you to a covered work, |
|||
for which you have or can give appropriate copyright permission. |
|||
|
|||
Notwithstanding any other provision of this License, for material you |
|||
add to a covered work, you may (if authorized by the copyright holders of |
|||
that material) supplement the terms of this License with terms: |
|||
|
|||
a) Disclaiming warranty or limiting liability differently from the |
|||
terms of sections 15 and 16 of this License; or |
|||
|
|||
b) Requiring preservation of specified reasonable legal notices or |
|||
author attributions in that material or in the Appropriate Legal |
|||
Notices displayed by works containing it; or |
|||
|
|||
c) Prohibiting misrepresentation of the origin of that material, or |
|||
requiring that modified versions of such material be marked in |
|||
reasonable ways as different from the original version; or |
|||
|
|||
d) Limiting the use for publicity purposes of names of licensors or |
|||
authors of the material; or |
|||
|
|||
e) Declining to grant rights under trademark law for use of some |
|||
trade names, trademarks, or service marks; or |
|||
|
|||
f) Requiring indemnification of licensors and authors of that |
|||
material by anyone who conveys the material (or modified versions of |
|||
it) with contractual assumptions of liability to the recipient, for |
|||
any liability that these contractual assumptions directly impose on |
|||
those licensors and authors. |
|||
|
|||
All other non-permissive additional terms are considered "further |
|||
restrictions" within the meaning of section 10. If the Program as you |
|||
received it, or any part of it, contains a notice stating that it is |
|||
governed by this License along with a term that is a further |
|||
restriction, you may remove that term. If a license document contains |
|||
a further restriction but permits relicensing or conveying under this |
|||
License, you may add to a covered work material governed by the terms |
|||
of that license document, provided that the further restriction does |
|||
not survive such relicensing or conveying. |
|||
|
|||
If you add terms to a covered work in accord with this section, you |
|||
must place, in the relevant source files, a statement of the |
|||
additional terms that apply to those files, or a notice indicating |
|||
where to find the applicable terms. |
|||
|
|||
Additional terms, permissive or non-permissive, may be stated in the |
|||
form of a separately written license, or stated as exceptions; |
|||
the above requirements apply either way. |
|||
|
|||
8. Termination. |
|||
|
|||
You may not propagate or modify a covered work except as expressly |
|||
provided under this License. Any attempt otherwise to propagate or |
|||
modify it is void, and will automatically terminate your rights under |
|||
this License (including any patent licenses granted under the third |
|||
paragraph of section 11). |
|||
|
|||
However, if you cease all violation of this License, then your |
|||
license from a particular copyright holder is reinstated (a) |
|||
provisionally, unless and until the copyright holder explicitly and |
|||
finally terminates your license, and (b) permanently, if the copyright |
|||
holder fails to notify you of the violation by some reasonable means |
|||
prior to 60 days after the cessation. |
|||
|
|||
Moreover, your license from a particular copyright holder is |
|||
reinstated permanently if the copyright holder notifies you of the |
|||
violation by some reasonable means, this is the first time you have |
|||
received notice of violation of this License (for any work) from that |
|||
copyright holder, and you cure the violation prior to 30 days after |
|||
your receipt of the notice. |
|||
|
|||
Termination of your rights under this section does not terminate the |
|||
licenses of parties who have received copies or rights from you under |
|||
this License. If your rights have been terminated and not permanently |
|||
reinstated, you do not qualify to receive new licenses for the same |
|||
material under section 10. |
|||
|
|||
9. Acceptance Not Required for Having Copies. |
|||
|
|||
You are not required to accept this License in order to receive or |
|||
run a copy of the Program. Ancillary propagation of a covered work |
|||
occurring solely as a consequence of using peer-to-peer transmission |
|||
to receive a copy likewise does not require acceptance. However, |
|||
nothing other than this License grants you permission to propagate or |
|||
modify any covered work. These actions infringe copyright if you do |
|||
not accept this License. Therefore, by modifying or propagating a |
|||
covered work, you indicate your acceptance of this License to do so. |
|||
|
|||
10. Automatic Licensing of Downstream Recipients. |
|||
|
|||
Each time you convey a covered work, the recipient automatically |
|||
receives a license from the original licensors, to run, modify and |
|||
propagate that work, subject to this License. You are not responsible |
|||
for enforcing compliance by third parties with this License. |
|||
|
|||
An "entity transaction" is a transaction transferring control of an |
|||
organization, or substantially all assets of one, or subdividing an |
|||
organization, or merging organizations. If propagation of a covered |
|||
work results from an entity transaction, each party to that |
|||
transaction who receives a copy of the work also receives whatever |
|||
licenses to the work the party's predecessor in interest had or could |
|||
give under the previous paragraph, plus a right to possession of the |
|||
Corresponding Source of the work from the predecessor in interest, if |
|||
the predecessor has it or can get it with reasonable efforts. |
|||
|
|||
You may not impose any further restrictions on the exercise of the |
|||
rights granted or affirmed under this License. For example, you may |
|||
not impose a license fee, royalty, or other charge for exercise of |
|||
rights granted under this License, and you may not initiate litigation |
|||
(including a cross-claim or counterclaim in a lawsuit) alleging that |
|||
any patent claim is infringed by making, using, selling, offering for |
|||
sale, or importing the Program or any portion of it. |
|||
|
|||
11. Patents. |
|||
|
|||
A "contributor" is a copyright holder who authorizes use under this |
|||
License of the Program or a work on which the Program is based. The |
|||
work thus licensed is called the contributor's "contributor version". |
|||
|
|||
A contributor's "essential patent claims" are all patent claims |
|||
owned or controlled by the contributor, whether already acquired or |
|||
hereafter acquired, that would be infringed by some manner, permitted |
|||
by this License, of making, using, or selling its contributor version, |
|||
but do not include claims that would be infringed only as a |
|||
consequence of further modification of the contributor version. For |
|||
purposes of this definition, "control" includes the right to grant |
|||
patent sublicenses in a manner consistent with the requirements of |
|||
this License. |
|||
|
|||
Each contributor grants you a non-exclusive, worldwide, royalty-free |
|||
patent license under the contributor's essential patent claims, to |
|||
make, use, sell, offer for sale, import and otherwise run, modify and |
|||
propagate the contents of its contributor version. |
|||
|
|||
In the following three paragraphs, a "patent license" is any express |
|||
agreement or commitment, however denominated, not to enforce a patent |
|||
(such as an express permission to practice a patent or covenant not to |
|||
sue for patent infringement). To "grant" such a patent license to a |
|||
party means to make such an agreement or commitment not to enforce a |
|||
patent against the party. |
|||
|
|||
If you convey a covered work, knowingly relying on a patent license, |
|||
and the Corresponding Source of the work is not available for anyone |
|||
to copy, free of charge and under the terms of this License, through a |
|||
publicly available network server or other readily accessible means, |
|||
then you must either (1) cause the Corresponding Source to be so |
|||
available, or (2) arrange to deprive yourself of the benefit of the |
|||
patent license for this particular work, or (3) arrange, in a manner |
|||
consistent with the requirements of this License, to extend the patent |
|||
license to downstream recipients. "Knowingly relying" means you have |
|||
actual knowledge that, but for the patent license, your conveying the |
|||
covered work in a country, or your recipient's use of the covered work |
|||
in a country, would infringe one or more identifiable patents in that |
|||
country that you have reason to believe are valid. |
|||
|
|||
If, pursuant to or in connection with a single transaction or |
|||
arrangement, you convey, or propagate by procuring conveyance of, a |
|||
covered work, and grant a patent license to some of the parties |
|||
receiving the covered work authorizing them to use, propagate, modify |
|||
or convey a specific copy of the covered work, then the patent license |
|||
you grant is automatically extended to all recipients of the covered |
|||
work and works based on it. |
|||
|
|||
A patent license is "discriminatory" if it does not include within |
|||
the scope of its coverage, prohibits the exercise of, or is |
|||
conditioned on the non-exercise of one or more of the rights that are |
|||
specifically granted under this License. You may not convey a covered |
|||
work if you are a party to an arrangement with a third party that is |
|||
in the business of distributing software, under which you make payment |
|||
to the third party based on the extent of your activity of conveying |
|||
the work, and under which the third party grants, to any of the |
|||
parties who would receive the covered work from you, a discriminatory |
|||
patent license (a) in connection with copies of the covered work |
|||
conveyed by you (or copies made from those copies), or (b) primarily |
|||
for and in connection with specific products or compilations that |
|||
contain the covered work, unless you entered into that arrangement, |
|||
or that patent license was granted, prior to 28 March 2007. |
|||
|
|||
Nothing in this License shall be construed as excluding or limiting |
|||
any implied license or other defenses to infringement that may |
|||
otherwise be available to you under applicable patent law. |
|||
|
|||
12. No Surrender of Others' Freedom. |
|||
|
|||
If conditions are imposed on you (whether by court order, agreement or |
|||
otherwise) that contradict the conditions of this License, they do not |
|||
excuse you from the conditions of this License. If you cannot convey a |
|||
covered work so as to satisfy simultaneously your obligations under this |
|||
License and any other pertinent obligations, then as a consequence you may |
|||
not convey it at all. For example, if you agree to terms that obligate you |
|||
to collect a royalty for further conveying from those to whom you convey |
|||
the Program, the only way you could satisfy both those terms and this |
|||
License would be to refrain entirely from conveying the Program. |
|||
|
|||
13. Use with the GNU Affero General Public License. |
|||
|
|||
Notwithstanding any other provision of this License, you have |
|||
permission to link or combine any covered work with a work licensed |
|||
under version 3 of the GNU Affero General Public License into a single |
|||
combined work, and to convey the resulting work. The terms of this |
|||
License will continue to apply to the part which is the covered work, |
|||
but the special requirements of the GNU Affero General Public License, |
|||
section 13, concerning interaction through a network will apply to the |
|||
combination as such. |
|||
|
|||
14. Revised Versions of this License. |
|||
|
|||
The Free Software Foundation may publish revised and/or new versions of |
|||
the GNU General Public License from time to time. Such new versions will |
|||
be similar in spirit to the present version, but may differ in detail to |
|||
address new problems or concerns. |
|||
|
|||
Each version is given a distinguishing version number. If the |
|||
Program specifies that a certain numbered version of the GNU General |
|||
Public License "or any later version" applies to it, you have the |
|||
option of following the terms and conditions either of that numbered |
|||
version or of any later version published by the Free Software |
|||
Foundation. If the Program does not specify a version number of the |
|||
GNU General Public License, you may choose any version ever published |
|||
by the Free Software Foundation. |
|||
|
|||
If the Program specifies that a proxy can decide which future |
|||
versions of the GNU General Public License can be used, that proxy's |
|||
public statement of acceptance of a version permanently authorizes you |
|||
to choose that version for the Program. |
|||
|
|||
Later license versions may give you additional or different |
|||
permissions. However, no additional obligations are imposed on any |
|||
author or copyright holder as a result of your choosing to follow a |
|||
later version. |
|||
|
|||
15. Disclaimer of Warranty. |
|||
|
|||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY |
|||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT |
|||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY |
|||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, |
|||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM |
|||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF |
|||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION. |
|||
|
|||
16. Limitation of Liability. |
|||
|
|||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |
|||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS |
|||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY |
|||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE |
|||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF |
|||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD |
|||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), |
|||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF |
|||
SUCH DAMAGES. |
|||
|
|||
17. Interpretation of Sections 15 and 16. |
|||
|
|||
If the disclaimer of warranty and limitation of liability provided |
|||
above cannot be given local legal effect according to their terms, |
|||
reviewing courts shall apply local law that most closely approximates |
|||
an absolute waiver of all civil liability in connection with the |
|||
Program, unless a warranty or assumption of liability accompanies a |
|||
copy of the Program in return for a fee. |
|||
|
|||
END OF TERMS AND CONDITIONS |
|||
|
|||
How to Apply These Terms to Your New Programs |
|||
|
|||
If you develop a new program, and you want it to be of the greatest |
|||
possible use to the public, the best way to achieve this is to make it |
|||
free software which everyone can redistribute and change under these terms. |
|||
|
|||
To do so, attach the following notices to the program. It is safest |
|||
to attach them to the start of each source file to most effectively |
|||
state the exclusion of warranty; and each file should have at least |
|||
the "copyright" line and a pointer to where the full notice is found. |
|||
|
|||
<one line to give the program's name and a brief idea of what it does.> |
|||
Copyright (C) <year> <name of author> |
|||
|
|||
This program is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
|
|||
Also add information on how to contact you by electronic and paper mail. |
|||
|
|||
If the program does terminal interaction, make it output a short |
|||
notice like this when it starts in an interactive mode: |
|||
|
|||
<program> Copyright (C) <year> <name of author> |
|||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. |
|||
This is free software, and you are welcome to redistribute it |
|||
under certain conditions; type `show c' for details. |
|||
|
|||
The hypothetical commands `show w' and `show c' should show the appropriate |
|||
parts of the General Public License. Of course, your program's commands |
|||
might be different; for a GUI interface, you would use an "about box". |
|||
|
|||
You should also get your employer (if you work as a programmer) or school, |
|||
if any, to sign a "copyright disclaimer" for the program, if necessary. |
|||
For more information on this, and how to apply and follow the GNU GPL, see |
|||
<http://www.gnu.org/licenses/>. |
|||
|
|||
The GNU General Public License does not permit incorporating your program |
|||
into proprietary programs. If your program is a subroutine library, you |
|||
may consider it more useful to permit linking proprietary applications with |
|||
the library. If this is what you want to do, use the GNU Lesser General |
|||
Public License instead of this License. But first, please read |
|||
<http://www.gnu.org/philosophy/why-not-lgpl.html>. |
@ -0,0 +1,5 @@ |
|||
#!/bin/bash |
|||
program=exampleClientCboxQtNsm #MUST be the same as engine/config.py shortName |
|||
cboxconfigure="--without-fluidsynth --without-libsmf" |
|||
|
|||
. template/configure.template #. is the posix compatible version of source |
@ -0,0 +1,13 @@ |
|||
[Desktop Entry] |
|||
Type=Application |
|||
Name=Laborejo |
|||
GenericName=Sequencer |
|||
Comment=MIDI sequencer based on classical music notation. |
|||
Comment[de]=MIDI-Sequencer mit Musiknoten |
|||
Exec=laborejo2 |
|||
Icon=laborejo |
|||
Terminal=false |
|||
StartupNotify=false |
|||
Categories=AudioVideo;Audio;X-Recorders;X-Multitrack;X-Jack; |
|||
X-NSM-capable=true |
|||
Version=2.0 |
After Width: | Height: | Size: 680 B |
After Width: | Height: | Size: 680 B |
@ -0,0 +1,430 @@ |
|||
from: https://creativecommons.org/licenses/by-sa/4.0/legalcode.txt |
|||
|
|||
Attribution-ShareAlike 4.0 International |
|||
|
|||
======================================================================= |
|||
|
|||
Creative Commons Corporation ("Creative Commons") is not a law firm and |
|||
does not provide legal services or legal advice. Distribution of |
|||
Creative Commons public licenses does not create a lawyer-client or |
|||
other relationship. Creative Commons makes its licenses and related |
|||
information available on an "as-is" basis. Creative Commons gives no |
|||
warranties regarding its licenses, any material licensed under their |
|||
terms and conditions, or any related information. Creative Commons |
|||
disclaims all liability for damages resulting from their use to the |
|||
fullest extent possible. |
|||
|
|||
Using Creative Commons Public Licenses |
|||
|
|||
Creative Commons public licenses provide a standard set of terms and |
|||
conditions that creators and other rights holders may use to share |
|||
original works of authorship and other material subject to copyright |
|||
and certain other rights specified in the public license below. The |
|||
following considerations are for informational purposes only, are not |
|||
exhaustive, and do not form part of our licenses. |
|||
|
|||
Considerations for licensors: Our public licenses are |
|||
intended for use by those authorized to give the public |
|||
permission to use material in ways otherwise restricted by |
|||
copyright and certain other rights. Our licenses are |
|||
irrevocable. Licensors should read and understand the terms |
|||
and conditions of the license they choose before applying it. |
|||
Licensors should also secure all rights necessary before |
|||
applying our licenses so that the public can reuse the |
|||
material as expected. Licensors should clearly mark any |
|||
material not subject to the license. This includes other CC- |
|||
licensed material, or material used under an exception or |
|||
limitation to copyright. More considerations for licensors: |
|||
wiki.creativecommons.org/Considerations_for_licensors |
|||
|
|||
Considerations for the public: By using one of our public |
|||
licenses, a licensor grants the public permission to use the |
|||
licensed material under specified terms and conditions. If |
|||
the licensor's permission is not necessary for any reason--for |
|||
example, because of any applicable exception or limitation to |
|||
copyright--then that use is not regulated by the license. Our |
|||
licenses grant only permissions under copyright and certain |
|||
other rights that a licensor has authority to grant. Use of |
|||
the licensed material may still be restricted for other |
|||
reasons, including because others have copyright or other |
|||
rights in the material. A licensor may make special requests, |
|||
such as asking that all changes be marked or described. |
|||
Although not required by our licenses, you are encouraged to |
|||
respect those requests where reasonable. More considerations |
|||
for the public: |
|||
wiki.creativecommons.org/Considerations_for_licensees |
|||
|
|||
======================================================================= |
|||
|
|||
Creative Commons Attribution-ShareAlike 4.0 International Public |
|||
License |
|||
|
|||
By exercising the Licensed Rights (defined below), You accept and agree |
|||
to be bound by the terms and conditions of this Creative Commons |
|||
Attribution-ShareAlike 4.0 International Public License ("Public |
|||
License"). To the extent this Public License may be interpreted as a |
|||
contract, You are granted the Licensed Rights in consideration of Your |
|||
acceptance of these terms and conditions, and the Licensor grants You |
|||
such rights in consideration of benefits the Licensor receives from |
|||
making the Licensed Material available under these terms and |
|||
conditions. |
|||
|
|||
|
|||
Section 1 -- Definitions. |
|||
|
|||
a. Adapted Material means material subject to Copyright and Similar |
|||
Rights that is derived from or based upon the Licensed Material |
|||
and in which the Licensed Material is translated, altered, |
|||
arranged, transformed, or otherwise modified in a manner requiring |
|||
permission under the Copyright and Similar Rights held by the |
|||
Licensor. For purposes of this Public License, where the Licensed |
|||
Material is a musical work, performance, or sound recording, |
|||
Adapted Material is always produced where the Licensed Material is |
|||
synched in timed relation with a moving image. |
|||
|
|||
b. Adapter's License means the license You apply to Your Copyright |
|||
and Similar Rights in Your contributions to Adapted Material in |
|||
accordance with the terms and conditions of this Public License. |
|||
|
|||
c. BY-SA Compatible License means a license listed at |
|||
creativecommons.org/compatiblelicenses, approved by Creative |
|||
Commons as essentially the equivalent of this Public License. |
|||
|
|||
d. Copyright and Similar Rights means copyright and/or similar rights |
|||
closely related to copyright including, without limitation, |
|||
performance, broadcast, sound recording, and Sui Generis Database |
|||
Rights, without regard to how the rights are labeled or |
|||
categorized. For purposes of this Public License, the rights |
|||
specified in Section 2(b)(1)-(2) are not Copyright and Similar |
|||
Rights. |
|||
|
|||
e. Effective Technological Measures means those measures that, in the |
|||
absence of proper authority, may not be circumvented under laws |
|||
fulfilling obligations under Article 11 of the WIPO Copyright |
|||
Treaty adopted on December 20, 1996, and/or similar international |
|||
agreements. |
|||
|
|||
f. Exceptions and Limitations means fair use, fair dealing, and/or |
|||
any other exception or limitation to Copyright and Similar Rights |
|||
that applies to Your use of the Licensed Material. |
|||
|
|||
g. License Elements means the license attributes listed in the name |
|||
of a Creative Commons Public License. The License Elements of this |
|||
Public License are Attribution and ShareAlike. |
|||
|
|||
h. Licensed Material means the artistic or literary work, database, |
|||
or other material to which the Licensor applied this Public |
|||
License. |
|||
|
|||
i. Licensed Rights means the rights granted to You subject to the |
|||
terms and conditions of this Public License, which are limited to |
|||
all Copyright and Similar Rights that apply to Your use of the |
|||
Licensed Material and that the Licensor has authority to license. |
|||
|
|||
j. Licensor means the individual(s) or entity(ies) granting rights |
|||
under this Public License. |
|||
|
|||
k. Share means to provide material to the public by any means or |
|||
process that requires permission under the Licensed Rights, such |
|||
as reproduction, public display, public performance, distribution, |
|||
dissemination, communication, or importation, and to make material |
|||
available to the public including in ways that members of the |
|||
public may access the material from a place and at a time |
|||
individually chosen by them. |
|||
|
|||
l. Sui Generis Database Rights means rights other than copyright |
|||
resulting from Directive 96/9/EC of the European Parliament and of |
|||
the Council of 11 March 1996 on the legal protection of databases, |
|||
as amended and/or succeeded, as well as other essentially |
|||
equivalent rights anywhere in the world. |
|||
|
|||
m. You means the individual or entity exercising the Licensed Rights |
|||
under this Public License. Your has a corresponding meaning. |
|||
|
|||
|
|||
Section 2 -- Scope. |
|||
|
|||
a. License grant. |
|||
|
|||
1. Subject to the terms and conditions of this Public License, |
|||
the Licensor hereby grants You a worldwide, royalty-free, |
|||
non-sublicensable, non-exclusive, irrevocable license to |
|||
exercise the Licensed Rights in the Licensed Material to: |
|||
|
|||
a. reproduce and Share the Licensed Material, in whole or |
|||
in part; and |
|||
|
|||
b. produce, reproduce, and Share Adapted Material. |
|||
|
|||
2. Exceptions and Limitations. For the avoidance of doubt, where |
|||
Exceptions and Limitations apply to Your use, this Public |
|||
License does not apply, and You do not need to comply with |
|||
its terms and conditions. |
|||
|
|||
3. Term. The term of this Public License is specified in Section |
|||
6(a). |
|||
|
|||
4. Media and formats; technical modifications allowed. The |
|||
Licensor authorizes You to exercise the Licensed Rights in |
|||
all media and formats whether now known or hereafter created, |
|||
and to make technical modifications necessary to do so. The |
|||
Licensor waives and/or agrees not to assert any right or |
|||
authority to forbid You from making technical modifications |
|||
necessary to exercise the Licensed Rights, including |
|||
technical modifications necessary to circumvent Effective |
|||
Technological Measures. For purposes of this Public License, |
|||
simply making modifications authorized by this Section 2(a) |
|||
(4) never produces Adapted Material. |
|||
|
|||
5. Downstream recipients. |
|||
|
|||
a. Offer from the Licensor -- Licensed Material. Every |
|||
recipient of the Licensed Material automatically |
|||
receives an offer from the Licensor to exercise the |
|||
Licensed Rights under the terms and conditions of this |
|||
Public License. |
|||
|
|||
b. Additional offer from the Licensor -- Adapted Material. |
|||
Every recipient of Adapted Material from You |
|||
automatically receives an offer from the Licensor to |
|||
exercise the Licensed Rights in the Adapted Material |
|||
under the conditions of the Adapter's License You apply. |
|||
|
|||
c. No downstream restrictions. You may not offer or impose |
|||
any additional or different terms or conditions on, or |
|||
apply any Effective Technological Measures to, the |
|||
Licensed Material if doing so restricts exercise of the |
|||
Licensed Rights by any recipient of the Licensed |
|||
Material. |
|||
|
|||
6. No endorsement. Nothing in this Public License constitutes or |
|||
may be construed as permission to assert or imply that You |
|||
are, or that Your use of the Licensed Material is, connected |
|||
with, or sponsored, endorsed, or granted official status by, |
|||
the Licensor or others designated to receive attribution as |
|||
provided in Section 3(a)(1)(A)(i). |
|||
|
|||
b. Other rights. |
|||
|
|||
1. Moral rights, such as the right of integrity, are not |
|||
licensed under this Public License, nor are publicity, |
|||
privacy, and/or other similar personality rights; however, to |
|||
the extent possible, the Licensor waives and/or agrees not to |
|||
assert any such rights held by the Licensor to the limited |
|||
extent necessary to allow You to exercise the Licensed |
|||
Rights, but not otherwise. |
|||
|
|||
2. Patent and trademark rights are not licensed under this |
|||
Public License. |
|||
|
|||
3. To the extent possible, the Licensor waives any right to |
|||
collect royalties from You for the exercise of the Licensed |
|||
Rights, whether directly or through a collecting society |
|||
under any voluntary or waivable statutory or compulsory |
|||
licensing scheme. In all other cases the Licensor expressly |
|||
reserves any right to collect such royalties. |
|||
|
|||
|
|||
Section 3 -- License Conditions. |
|||
|
|||
Your exercise of the Licensed Rights is expressly made subject to the |
|||
following conditions. |
|||
|
|||
a. Attribution. |
|||
|
|||
1. If You Share the Licensed Material (including in modified |
|||
form), You must: |
|||
|
|||
a. retain the following if it is supplied by the Licensor |
|||
with the Licensed Material: |
|||
|
|||
i. identification of the creator(s) of the Licensed |
|||
Material and any others designated to receive |
|||
attribution, in any reasonable manner requested by |
|||
the Licensor (including by pseudonym if |
|||
designated); |
|||
|
|||
ii. a copyright notice; |
|||
|
|||
iii. a notice that refers to this Public License; |
|||
|
|||
iv. a notice that refers to the disclaimer of |
|||
warranties; |
|||
|
|||
v. a URI or hyperlink to the Licensed Material to the |
|||
extent reasonably practicable; |
|||
|
|||
b. indicate if You modified the Licensed Material and |
|||
retain an indication of any previous modifications; and |
|||
|
|||
c. indicate the Licensed Material is licensed under this |
|||
Public License, and include the text of, or the URI or |
|||
hyperlink to, this Public License. |
|||
|
|||
2. You may satisfy the conditions in Section 3(a)(1) in any |
|||
reasonable manner based on the medium, means, and context in |
|||
which You Share the Licensed Material. For example, it may be |
|||
reasonable to satisfy the conditions by providing a URI or |
|||
hyperlink to a resource that includes the required |
|||
information. |
|||
|
|||
3. If requested by the Licensor, You must remove any of the |
|||
information required by Section 3(a)(1)(A) to the extent |
|||
reasonably practicable. |
|||
|
|||
b. ShareAlike. |
|||
|
|||
In addition to the conditions in Section 3(a), if You Share |
|||
Adapted Material You produce, the following conditions also apply. |
|||
|
|||
1. The Adapter's License You apply must be a Creative Commons |
|||
license with the same License Elements, this version or |
|||
later, or a BY-SA Compatible License. |
|||
|
|||
2. You must include the text of, or the URI or hyperlink to, the |
|||
Adapter's License You apply. You may satisfy this condition |
|||
in any reasonable manner based on the medium, means, and |
|||
context in which You Share Adapted Material. |
|||
|
|||
3. You may not offer or impose any additional or different terms |
|||
or conditions on, or apply any Effective Technological |
|||
Measures to, Adapted Material that restrict exercise of the |
|||
rights granted under the Adapter's License You apply. |
|||
|
|||
|
|||
Section 4 -- Sui Generis Database Rights. |
|||
|
|||
Where the Licensed Rights include Sui Generis Database Rights that |
|||
apply to Your use of the Licensed Material: |
|||
|
|||
a. for the avoidance of doubt, Section 2(a)(1) grants You the right |
|||
to extract, reuse, reproduce, and Share all or a substantial |
|||
portion of the contents of the database; |
|||
|
|||
b. if You include all or a substantial portion of the database |
|||
contents in a database in which You have Sui Generis Database |
|||
Rights, then the database in which You have Sui Generis Database |
|||
Rights (but not its individual contents) is Adapted Material, |
|||
|
|||
including for purposes of Section 3(b); and |
|||
c. You must comply with the conditions in Section 3(a) if You Share |
|||
all or a substantial portion of the contents of the database. |
|||
|
|||
For the avoidance of doubt, this Section 4 supplements and does not |
|||
replace Your obligations under this Public License where the Licensed |
|||
Rights include other Copyright and Similar Rights. |
|||
|
|||
|
|||
Section 5 -- Disclaimer of Warranties and Limitation of Liability. |
|||
|
|||
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE |
|||
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS |
|||
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF |
|||
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, |
|||
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, |
|||
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR |
|||
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, |
|||
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT |
|||
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT |
|||
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. |
|||
|
|||
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE |
|||
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, |
|||
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, |
|||
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, |
|||
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR |
|||
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN |
|||
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR |
|||
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR |
|||
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. |
|||
|
|||
c. The disclaimer of warranties and limitation of liability provided |
|||
above shall be interpreted in a manner that, to the extent |
|||
possible, most closely approximates an absolute disclaimer and |
|||
waiver of all liability. |
|||
|
|||
|
|||
Section 6 -- Term and Termination. |
|||
|
|||
a. This Public License applies for the term of the Copyright and |
|||
Similar Rights licensed here. However, if You fail to comply with |
|||
this Public License, then Your rights under this Public License |
|||
terminate automatically. |
|||
|
|||
b. Where Your right to use the Licensed Material has terminated under |
|||
Section 6(a), it reinstates: |
|||
|
|||
1. automatically as of the date the violation is cured, provided |
|||
it is cured within 30 days of Your discovery of the |
|||
violation; or |
|||
|
|||
2. upon express reinstatement by the Licensor. |
|||
|
|||
For the avoidance of doubt, this Section 6(b) does not affect any |
|||
right the Licensor may have to seek remedies for Your violations |
|||
of this Public License. |
|||
|
|||
c. For the avoidance of doubt, the Licensor may also offer the |
|||
Licensed Material under separate terms or conditions or stop |
|||
distributing the Licensed Material at any time; however, doing so |
|||
will not terminate this Public License. |
|||
|
|||
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public |
|||
License. |
|||
|
|||
|
|||
Section 7 -- Other Terms and Conditions. |
|||
|
|||
a. The Licensor shall not be bound by any additional or different |
|||
terms or conditions communicated by You unless expressly agreed. |
|||
|
|||
b. Any arrangements, understandings, or agreements regarding the |
|||
Licensed Material not stated herein are separate from and |
|||
independent of the terms and conditions of this Public License. |
|||
|
|||
|
|||
Section 8 -- Interpretation. |
|||
|
|||
a. For the avoidance of doubt, this Public License does not, and |
|||
shall not be interpreted to, reduce, limit, restrict, or impose |
|||
conditions on any use of the Licensed Material that could lawfully |
|||
be made without permission under this Public License. |
|||
|
|||
b. To the extent possible, if any provision of this Public License is |
|||
deemed unenforceable, it shall be automatically reformed to the |
|||
minimum extent necessary to make it enforceable. If the provision |
|||
cannot be reformed, it shall be severed from this Public License |
|||
without affecting the enforceability of the remaining terms and |
|||
conditions. |
|||
|
|||
c. No term or condition of this Public License will be waived and no |
|||
failure to comply consented to unless expressly agreed to by the |
|||
Licensor. |
|||
|
|||
d. Nothing in this Public License constitutes or may be interpreted |
|||
as a limitation upon, or waiver of, any privileges and immunities |
|||
that apply to the Licensor or You, including from the legal |
|||
processes of any jurisdiction or authority. |
|||
|
|||
|
|||
======================================================================= |
|||
|
|||
Creative Commons is not a party to its public |
|||
licenses. Notwithstanding, Creative Commons may elect to apply one of |
|||
its public licenses to material it publishes and in those instances |
|||
will be considered the “Licensor.” The text of the Creative Commons |
|||
public licenses is dedicated to the public domain under the CC0 Public |
|||
Domain Dedication. Except for the limited purpose of indicating that |
|||
material is shared under a Creative Commons public license or as |
|||
otherwise permitted by the Creative Commons policies published at |
|||
creativecommons.org/policies, Creative Commons does not authorize the |
|||
use of the trademark "Creative Commons" or any other trademark or logo |
|||
of Creative Commons without its prior written consent including, |
|||
without limitation, in connection with any unauthorized modifications |
|||
to any of its public licenses or any other arrangements, |
|||
understandings, or agreements concerning use of licensed material. For |
|||
the avoidance of doubt, this paragraph does not form part of the |
|||
public licenses. |
|||
|
|||
Creative Commons may be contacted at creativecommons.org. |
|||
|
@ -0,0 +1,7 @@ |
|||
Run ./build-documentation.sh for html output in the /out directory. |
|||
|
|||
.adoc is asciidoctor, not simple asciidoc. |
|||
|
|||
This documentation is licensed under Creative Commons-BY-SA-4.0. |
|||
Please read the provided documentation/LICENSE file or visit |
|||
https://creativecommons.org/licenses/by-sa/4.0/legalcode |
@ -0,0 +1,18 @@ |
|||
#!/bin/sh |
|||
|
|||
#The documentation is built statically and does not belong to the normal build process with configure and make |
|||
#Its updating is part of the development process, not packaging and running. |
|||
#The correct out/ dir is already part of git. |
|||
|
|||
set -e |
|||
asciidoctor index.adoc -o out/index.html |
|||
|
|||
asciidoctor german.adoc -o out/german.html |
|||
cp overview-german.png out/overview-german.png |
|||
|
|||
asciidoctor english.adoc -o out/english.html |
|||
cp overview-english.png out/overview-english.png |
|||
|
|||
#cp *-quickstart*.mp4 out/ |
|||
|
|||
cp favicon.* out/ |
@ -0,0 +1,5 @@ |
|||
== Usage |
|||
* Nothing |
|||
* To |
|||
* See |
|||
* Here |
@ -0,0 +1,5 @@ |
|||
== Bedienung |
|||
Hier gibt es nichts zu sehen. |
|||
|
|||
* Weiter |
|||
* Gehen |
@ -0,0 +1,31 @@ |
|||
#! /usr/bin/env python3 |
|||
# -*- coding: utf-8 -*- |
|||
""" |
|||
Copyright 2018, Nils Hilbricht, Germany ( https://www.hilbricht.net ) |
|||
|
|||
This file is part of the Laborejo Software Suite ( https://www.laborejo.org ), |
|||
more specifically its template base application. |
|||
|
|||
The Template Base Application is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
""" |
|||
|
|||
import logging; logging.info("import {}".format(__file__)) |
|||
|
|||
#This file only exists as a reminder to _not_ create it again wrongly in the future. |
|||
|
|||
#from .api import * #Do not star-import here! |
|||
#This leads to uncontrollable behaviour because importing _any_ file from engine will first import this init file, which would trigger the api to start its session. |
|||
#Instead use explicit import to get the api. |
|||
|
|||
#No!: import engine.api as api #This loads the engine and starts a session. |
@ -0,0 +1,331 @@ |
|||
#! /usr/bin/env python3 |
|||
# -*- coding: utf-8 -*- |
|||
""" |
|||
Copyright 2017, Nils Hilbricht, Germany ( https://www.hilbricht.net ) |
|||
|
|||
This file is part of Laborejo ( https://www.laborejo.org ) |
|||
|
|||
Laborejo is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
""" |
|||
|
|||
from weakref import WeakSet |
|||
from weakref import ref as weakref_ref |
|||
from .items import * #loading from file needs all items. |
|||
|
|||
class Block(object): |
|||
#allBlocks = WeakValueDictionary() #key is the blockId, value is the weak reference to the Block |
|||
allBlocks = {} #key is the blockId, value is the Block. This is a one way dict. It gets never deleted so undo can recover old blocks. Since blocks are unique this is no problem. |
|||
#NEVER!! iteratre over allBlocks if you don't know what you are doing. This contains deleted blocks as well. |
|||
firstBlockWithNewContentDuringDeserializeToObject = dict() #this is not resetted anywhere since each load is a program start. |
|||
|
|||
def __init__(self, track): |
|||
self.data = list() |
|||
self.name = str(id(self)) |
|||
self._minimumInTicks = [0] # if this is bigger than the actual duration-sum of the content this will be used instead. Advice: best used in the form of x*210 (multiple of real base-durations) #is content linked, thats why it is a mutable list of len==1. |
|||
self.linkedContentBlocks = WeakSet() #only new standalone blocks use this empty WeakSet. once you contentLink a block it will be overwritten. |
|||
self._secondInit(parentTrack = track) |
|||
|
|||
def _secondInit(self, parentTrack): |
|||
"""see Score._secondInit""" |
|||
self._parentTrack = weakref_ref(parentTrack) #a block can only be in one track. |
|||
self.localCursorIndex = 0 |
|||
self.linkedContentBlocks.add(self) |
|||
self.rememberBlock() |
|||
|
|||
@property |
|||
def minimumInTicks(self): |
|||
return self._minimumInTicks[0] |
|||
|
|||
@minimumInTicks.setter |
|||
def minimumInTicks(self, newValue): |
|||
"""Keep the mutable list at all cost""" |
|||
listId = id(self._minimumInTicks) |
|||
self._minimumInTicks.pop() |
|||
self._minimumInTicks.append(newValue) |
|||
assert len(self._minimumInTicks) == 1 |
|||
assert listId == id(self._minimumInTicks) |
|||
|
|||
@classmethod |
|||
def instanceFromSerializedData(cls, serializedObject, parentObject): |
|||
"""see Score.instanceFromSerializedData""" |
|||
assert cls.__name__ == serializedObject["class"] |
|||
self = cls.__new__(cls) |
|||
if serializedObject["data"] is None: #Found a content linked block which already has one member of its group in the score |
|||
firstBlock = Block.firstBlockWithNewContentDuringDeserializeToObject[serializedObject["contentLinkGroup"]] #block with the same contentGroup. This is the one with the real data. |
|||
self.data = firstBlock.data |
|||
self._minimumInTicks = firstBlock._minimumInTicks |
|||
self.linkedContentBlocks = firstBlock.linkedContentBlocks #add self to this is in _secondInit |
|||
else: #found a stand-alone block or the first one of a content link group |
|||
self.linkedContentBlocks = WeakSet() |
|||
self.data = [eval(item["class"]).instanceFromSerializedData(item, parentObject = self) for item in serializedObject["data"]] |
|||
Block.firstBlockWithNewContentDuringDeserializeToObject[serializedObject["contentLinkGroup"]] = self |
|||
for item in self.data: |
|||
item.parentBlocks.add(self) |
|||
self.name = serializedObject["name"] |
|||
self._minimumInTicks = [int(serializedObject["minimumInTicks"])] #saved as int, used as list |
|||
self._secondInit(parentTrack = parentObject) |
|||
return self |
|||
|
|||
def serialize(self): |
|||
"""Return a serialized data from this instance. |
|||
Used for save and load. |
|||
|
|||
Can be called in a chain by subclasses with super().serialize |
|||
|
|||
The main difference between serialize and exportObject is that |
|||
serialize does not compute anything. I just saves the state |
|||
without calulating note on and off or stem directions, |
|||
for example. |
|||
""" |
|||
#result = super()._serialize() #call this in child classes |
|||
result = {} |
|||
result["class"] = self.__class__.__name__ |
|||
result["name"] = self.name |
|||
result["minimumInTicks"] = self.minimumInTicks |
|||
|
|||
#We only save the data if this is the first content-linked block in a sequence. |
|||
contentLinkGroupId = id(self.data) |
|||
result["contentLinkGroup"] = contentLinkGroupId |
|||
|
|||
for block in self.parentTrack.parentData.allBlocks(): |
|||
blockId = id(block) |
|||
dataId = id(block.data) |
|||
if dataId == contentLinkGroupId: |
|||
if blockId == id(self): #first block with this dataId found. |
|||
result["data"] = [item.serialize() for item in self.data] |
|||
else: #content linked, but not the first. Block already serialized. |
|||
result["data"] = None #we don't need to do anything more. The rest is handled by load and instanceFromSerializedData |
|||
break |
|||
#else: |
|||
#loop ran through. This never happens. |
|||
return result |
|||
|
|||
def rememberBlock(self): |
|||
oid = id(self) |
|||
Block.allBlocks[oid] = self #This is on the score level, or a global level. That means we don't need to change this, even if the track gets moved to a new track by the api. |
|||
#weakref_finalize(self, print, "deleted block "+str(oid)) |
|||
return oid |
|||
|
|||
@property |
|||
def parentTrack(self): |
|||
if self._parentTrack: |
|||
return self._parentTrack() #return the real parentTrack |
|||
else: |
|||
assert self._parentTrack is None |
|||
return self._parentTrack |
|||
|
|||
@parentTrack.setter |
|||
def parentTrack(self, newTrack): |
|||
if not self._parentTrack: #because it was None/not in a track |
|||
self._parentTrack = WeakSet() |
|||
if newTrack: |
|||
self._parentTrack = weakref_ref(newTrack) |
|||
else: |
|||
self._parentTrack = None |
|||
|
|||
def copy(self, newParentTrack): |
|||
"""Return an independet copy of this block. |
|||
It will not be inserted into a track here but in the parentTrack |
|||
|
|||
It is by design only possible that a block will be inserted |
|||
in the same track, next to the original block. |
|||
It can be moved later by the api module which will reset |
|||
the parentTrack. But at first the parentTrack stays the same.""" |
|||
new = Block(newParentTrack) |
|||
assert newParentTrack |
|||
|
|||
#Do not change new.linkedContentBlocks! Copy creates a stand alone copy. |
|||
assert len(new.linkedContentBlocks) == 1 |
|||
|
|||
for item in self.data: |
|||
copyItem = item.copy() |
|||
new.data.append(copyItem) |
|||
|
|||
copyItem.parentBlocks.add(new) #parentBlock was empty until now |
|||
if self in copyItem.parentBlocks: #TODO: investigate |
|||
copyItem.parentBlocks.remove(self) |
|||
|
|||
if self.name.endswith("-copy"): |
|||
new.name = self.name |
|||
else: |
|||
new.name = self.name + "-copy" |
|||
new._minimumInTicks = self._minimumInTicks[:] #mutable |
|||
return new |
|||
|
|||
def getUnlinkedData(self): |
|||
"""Set and handled for undo/redo by the api""" |
|||
newData = [] |
|||
newParentBlocks = WeakSet() |
|||
newParentBlocks.add(self) |
|||
|
|||
for item in self.data: |
|||
copyItem = item.copy() |
|||
newData.append(copyItem) |
|||
copyItem.parentBlocks = newParentBlocks |
|||
|
|||
assert len(newData) == len(self.data) |
|||
return newData |
|||
|
|||
|
|||
def getDataAsDict(self): |
|||
return { "name" : self.name, |
|||
"minimumInTicks" : self.minimumInTicks, |
|||
} |
|||
|
|||
def putDataFromDict(self, dataDict): |
|||
"""modify inplace. Useful for a gui function. Compatible with |
|||
the data from getDataAsDict""" |
|||
self.name = dataDict["name"] |
|||
self.minimumInTicks = dataDict["minimumInTicks"] |
|||
|
|||
def contentLink(self): |
|||
"""Return a copy where only certain parameters |
|||
like Content are linked. Others can be changed. |
|||
It will not be inserted into a track here but in the parentTrack |
|||
|
|||
It is by design only possible that a block will be inserted |
|||
in the same track, next to the original block. |
|||
It can be moved later by the api module which will reset |
|||
the parentTrack. But at first the parentTrack stays the same. |
|||
""" |
|||
assert self.parentTrack |
|||
new = Block(self.parentTrack) |
|||
new.linkedContentBlocks = self.linkedContentBlocks |
|||
new.linkedContentBlocks.add(new) |
|||
new.data = self.data #mutable. Will change in all blocks together. |
|||
new.name = self.name #immutable. Will change independently but we start with the same name. |
|||
new._minimumInTicks = self._minimumInTicks #mutable |
|||
#Add the new block to the parentBlocks set but don't delete self from it, as in copy(). The items are now in more than one block simultaniously |
|||
for item in new.data: |
|||
item.parentBlocks.add(new) |
|||
return new |
|||
|
|||
def linkedContentBlocksInScore(self): |
|||
"""filters linkedContentBlocks to only include |
|||
those currently in the score. Not those in the undo repository |
|||
""" |
|||
return (block for block in self.linkedContentBlocks if block.parentTrack) |
|||
|
|||
def duration(self): |
|||
"""The first block might have an upbeat. We check that here and not in the track.""" |
|||
|
|||
actualBlockDuration = 0 |
|||
for item in self.data: |
|||
actualBlockDuration += item.logicalDuration() |
|||
|
|||
if actualBlockDuration >= self.minimumInTicks: |
|||
return actualBlockDuration |
|||
else: |
|||
return self.minimumInTicks |
|||
|
|||
def staticExportEndMarkerDuration(self): |
|||
actualBlockDuration = 0 |
|||
for item in self.data: |
|||
actualBlockDuration += item.logicalDuration() |
|||
if actualBlockDuration >= self.minimumInTicks: #this also guarantees that the substraction below is > 0. |
|||
return 0 |
|||
else: |
|||
return self.minimumInTicks - actualBlockDuration #this is the difference to self.duration() |
|||
|
|||
def position(self): |
|||
"""the position of the subcursor in this block""" |
|||
return self.localCursorIndex |
|||
|
|||
def left(self): |
|||
if self.localCursorIndex > 0: |
|||
self.localCursorIndex -= 1 |
|||
return True |
|||
else: |
|||
return False #Already at the start. |
|||
|
|||
def right(self): |
|||
if not self.isAppending(): |
|||
self.localCursorIndex += 1 |
|||
return True |
|||
else: |
|||
return False #Already at the end. |
|||
|
|||
def head(self): |
|||
self.localCursorIndex = 0 |
|||
|
|||
def tail(self): |
|||
self.localCursorIndex = len(self.data) #eventhough len counts from 1 and the cursorIndex from 0 we want exactly to be after the last item in data. |
|||
|
|||
def goToItem(self, itemInstance): |
|||
if itemInstance: |
|||
self.head() |
|||
while self.right(): |
|||
item = self.currentItem() |
|||
if item is itemInstance: |
|||
return True |
|||
else: |
|||
raise ValueError("Item not in this block. ", self, itemInstance) |
|||
elif itemInstance is None: |
|||
self.tail() |
|||
else: |
|||
raise ValueError("You must go to an item or None, not to: ", itemInstance) |
|||
|
|||
|
|||
def currentItem(self): |
|||
"""Can be used with goToItem""" |
|||
if not self.isAppending(): |
|||
return self.data[self.localCursorIndex] |
|||
else: |
|||
return None |
|||
|
|||
def previousItem(self): |
|||
"""Can be used with goToItem""" |
|||
if self.localCursorIndex > 0: |
|||
return self.data[self.localCursorIndex-1] |
|||
else: |
|||
return None |
|||
|
|||
def nextItem(self): |
|||
"""Can be used with goToItem""" |
|||
if self.localCursorIndex+1 >= len(self.data): #one before appending or appending itself |
|||
return None |
|||
else: |
|||
return self.data[self.localCursorIndex+1] |
|||
|
|||
def insert(self, item): |
|||
self.data.insert(self.localCursorIndex, item) #we do not need to check if appending or not. list.insert appends if the index is higher then len() |
|||
#self.localCursorIndex += 1 #we don't need to go right here because track.insert() is calling its own right() directly after insert, which triggers block.right() |
|||
item.parentBlocks.add(self) |
|||
|
|||
def delete(self): |
|||
"""The commented out is the immediate garbage collector which |
|||
made sure that deleted item are not in the allNotes weakref |
|||
dict any more. However, this does not happen any more since |
|||
we introduced undo which keeps a saved version. |
|||
For consitency reasons we chose not to save a copy in the undo |
|||
register (return ...copy()) but the real item. |
|||
|
|||
If this ever leads to problems with the weakref dict we must |
|||
reintroduce the copy""" |
|||
if not self.isAppending(): |
|||
result = self.data[self.localCursorIndex] |
|||
del self.data[self.localCursorIndex] |
|||
#we don't need to delete self from item.parentBlocks. It is automatically deleted in all contentLinked blocks, of course including its parentBlocks WeakSet() |
|||
return result |
|||
|
|||
def isAppending(self): |
|||
if self.localCursorIndex == len(self.data): #len counts from 1 and the cursorIndex from 0. So if they are the same we are one cursor position right of last item |
|||
return True |
|||
else: |
|||
return False |
|||
|
|||
def lilypond(self): |
|||
"""Called by track.lilypond(), returns a string""" |
|||
return " ".join(item.lilypond() for item in self.data) |
|||
|
@ -0,0 +1,56 @@ |
|||
#! /usr/bin/env python3 |
|||
# -*- coding: utf-8 -*- |
|||
|
|||
#Do not change these during runtime! |
|||
|
|||
METADATA={ |
|||
#The pretty name of this program. Used for NSM display and Jack client name |
|||
#Can contain everything a linux file/path supports. Never change this or it will break the |
|||
#session, making your file unable to load and destroying saved Jack connections. |
|||
"name" : "Laborejo", |
|||
|
|||
#Set this to the name the user types into a terminal. |
|||
#MUST be the same as the binary name as well as the name in configure. |
|||
#Program reports that as proc title so you can killall it by name. |
|||
#Should not contain spaces or special characters. We use this as save file extension as well |
|||
#to distinguish between compatible program versions. In basic programs this will just be e.g. |
|||
#patroneo. But in complex programs with a bright future it will be "laborejo1" "laborejo2" etc. |
|||
"shortName" : "laborejo2", |
|||
|
|||
"version" : "2.0", |
|||
"year" : "2019", |
|||
"author" : "Laborejo Software Suite", |
|||
"url" : "https://www.laborejo.org/laborejo", |
|||
|
|||
"supportedLanguages" : {"German":"de.qm"}, |
|||
|
|||
#Show the About Dialog the first time the program starts up. This is the initial state for a |
|||
#new instance in NSM, not the saved state! Decide on how annoying it would be for every new |
|||
#instance to show about. Fluajho does not show it because you add it many times into a session. |
|||
#Patroneo does because its only added once. |
|||
"showAboutDialogFirstStart" : True, |
|||
|
|||
#If your program handles very small duration with n-tuplets you should increase D4. |
|||
#This will not be visible to the outside jack world |
|||
"quarterNoteInTicks" : 53760, |
|||
|
|||
#How many audio outputs do you want? must be pairs. These are just unconnected jack outputs |
|||
#that need to be connected internally to instrument outputs like fluidsynth |
|||
"cboxOutputs" : 2 * 0, |
|||
|
|||
#Various strings for the README |
|||
#Extra whitespace will be stripped so we don't need to worry about docstring indentation |
|||
"description" : """ |
|||
Laborejo (Esperanto: Workshop) is a MIDI sequencer based on classical music notation. |
|||
Its main purpose is to compose and produce "traditional" music, such as instrumental pieces, |
|||
soundtracks and other music normally played back by samplers. |
|||
""" + "\n" + """ |
|||
Unlike other notation editors Laborejo is not meant primarily to print out sheets of notation but |
|||
to create music within your computer: You get all the tools you know from other midi sequencers for |
|||
maximum fine control to get exactly the music you want! |
|||
""" + "\n" + """ |
|||
Working in Laborejo is very fast and efficient by using a combination of midi input and typing.""", |
|||
|
|||
"dependencies" : "\n".join("* "+dep for dep in ("A Brain", "Linux" )), |
|||
|
|||
} |
@ -0,0 +1,142 @@ |
|||
#! /usr/bin/env python3 |
|||
# -*- coding: utf-8 -*- |
|||
""" |
|||
Copyright 2019, Nils Hilbricht, Germany ( https://www.hilbricht.net ) |
|||
|
|||
This file is part of the Laborejo Software Suite ( https://www.laborejo.org ), |
|||
more specifically its template base application. |
|||
|
|||
The Template Base Application is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
""" |
|||
|
|||
import logging; logging.info("import {}".format(__file__)) |
|||
|
|||
#Standard Library Modules |
|||
|
|||
#Third Party Modules |
|||
|
|||
#Template Modules |
|||
import template.engine.pitch as pitch |
|||
import template.engine.duration as duration |
|||
|
|||
#Our modules |
|||
class Cursor: |
|||
"""There is only one cursor in the program. |
|||
Each track has its own state with its own gap, tickindex etc. |
|||
but the cursor is unique. |
|||
Used for pitch and states which apply to all tracks, like |
|||
"insert next note with dot?" |
|||
|
|||
The cursor moves 'on stafflines' not through the keysig. Adaption to the keysig is done |
|||
by the api like in insertCursorCode or insertKeysignature |
|||
""" |
|||
def __init__(self, score): |
|||
self.score = score |
|||
self.pitchindex = pitch.ly2pitch["c'"] |
|||
self.prevailingBaseDuration = duration.D4 |
|||
self.persistentPrevailingDot = False #For the sake of simplicity this is only a bool. No "two prevailing dots" |
|||
self.oneTimePrevailingDotInverter = False #effects persistentPrevailingDot |
|||
|
|||
def up(self): |
|||
"""Move the cursor/pitchindex up, +50. |
|||
The cursor always goes up and down the c major scale. |
|||
Call with Cursor.up(). Class method """ |
|||
self.pitchindex += pitch.STEP |
|||
self.correctPitchindex() |
|||
|
|||
def down(self): |
|||
"""Move the cursor/pitchindex down, -50. |
|||
The cursor always goes up and down the c major scale. |
|||
Call with Cursor.down(). Class method""" |
|||
self.pitchindex -= pitch.STEP |
|||
self.correctPitchindex() |
|||
|
|||
def upOctave(self): |
|||
"""Move the cursor/pitchindex up, +350. |
|||
Call with Cursor.upOctave(). Class method""" |
|||
self.pitchindex += pitch.OCTAVE |
|||
self.correctPitchindex() |
|||
|
|||
def downOctave(self): |
|||
"""Move the cursor/pitchindex down, -350. |
|||
Call with Cursor.downOctave(). Class method""" |
|||
self.pitchindex -= pitch.OCTAVE |
|||
self.correctPitchindex() |
|||
|
|||
def correctPitchindex(self): |
|||
if self.pitchindex >= pitch.MAX: |
|||
self.pitchindex = pitch.MAX |
|||
elif self.pitchindex <= pitch.MIN: |
|||
self.pitchindex = pitch.MIN |
|||
else: |
|||
self.pitchindex = pitch.toWhite[self.pitchindex] |
|||
|
|||
def getPrevailingDot(self): |
|||
"""0 or 1. |
|||
Also toggles oneTimePrevailingDotInverter""" |
|||
if self.persistentPrevailingDot: |
|||
if self.oneTimePrevailingDotInverter: |
|||
self.oneTimePrevailingDotInverter = not self.oneTimePrevailingDotInverter |
|||
return 0 |
|||
else: |
|||
return 1 |
|||
else: |
|||
if self.oneTimePrevailingDotInverter: |
|||
self.oneTimePrevailingDotInverter = not self.oneTimePrevailingDotInverter |
|||
return 1 |
|||
else: |
|||
return 0 |
|||
|
|||
def distanceInDiatonicStepsFromTrebleB(self): |
|||
return pitch.distanceInDiatonicSteps(pitch.ly2pitch["b'"], self.pitchindex) #offset from the middle line in treble clef h', which is 0. c'' is -1, a' is +1 |
|||
|
|||
def asDotOnLine(self, clef): |
|||
return self.distanceInDiatonicStepsFromTrebleB() + clef.asDotOnLineOffset |
|||
|
|||
def exportObject(self, trackState): |
|||
"""Every time the cursor moves this will be called. |
|||
|
|||
return a dict which is a good layout |
|||
export basis. For example for GUI frontends. They don't have to |
|||
parse and calculate their own values in slow pure Python then. |
|||
|
|||
This is for GUIs etc. not for saving to JSOn. The cursor is not |
|||
saved. |
|||
""" |
|||
tempoItem = trackState.track.parentData.tempoTrack.tempoAtTickPosition(trackState.tickindex) #TODO: maybe get that from a cached version. |
|||
item = trackState.track.currentItem() |
|||
block = trackState.track.currentBlock() |
|||
|
|||
return { |
|||
"type": "Cursor", |
|||
"trackIndex": trackState.index(), |
|||
"track" : trackState.track, |
|||
"cboxMidiOutUuid" : trackState.track.sequencerInterface.cboxMidiOutUuid, #used for midi throught. Step midi shall produce sound through the current track. |
|||
"trackId" : id(trackState.track), |
|||
"position" : trackState.position(), |
|||
"tickindex" : trackState.tickindex, |
|||
"dotOnLine": self.asDotOnLine(trackState.clef()), |
|||
"pitch" : self.pitchindex, |
|||
"lilypondPitch" : pitch.pitch2ly[self.pitchindex], |
|||
"position" : trackState.position(), |
|||
"appending" : trackState.isAppending(), |
|||
"blockindex" : trackState.blockindex, |
|||
"block" : block, |
|||
"localCursorIndex" : block.localCursorIndex, |
|||
"itemId" : id(item), |
|||
"item" : item, |
|||
"tempoUnitsPerMinute" : tempoItem.unitsPerMinute, |
|||
"tempoReferenceTicks" : tempoItem.referenceTicks, |
|||
"tempoGraphType" : tempoItem.graphType, |
|||
} |
@ -0,0 +1,142 @@ |
|||
#! /usr/bin/env python3 |
|||
# -*- coding: utf-8 -*- |
|||
""" |
|||
Copyright 2017, Nils Hilbricht, Germany ( https://www.hilbricht.net ) |
|||
|
|||
This file is part of Laborejo ( https://www.laborejo.org ) |
|||
|
|||
Laborejo is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
""" |
|||
|
|||
"""Use generated music data and build a complete lilypond file from it""" |
|||
|
|||
import os.path |
|||
from datetime import date |
|||
import subprocess |
|||
from tempfile import gettempdir |
|||
da = date.fromordinal(730920) # 730920th day after 1. 1. 0001 |
|||
|
|||
def saveAsLilypond(score, absoluteFilePath = None): |
|||
#absoluteFilePath = self.absoluteFilePath + ".ly" if (not absoluteFilePath) else absoluteFilePath |
|||
assert absoluteFilePath.endswith(".ly") |
|||
result = score.lilypond() |
|||
if result: |
|||
with open(absoluteFilePath, "w", encoding="utf-8") as f: |
|||
f.write(result) |
|||
return absoluteFilePath |
|||
|
|||
def saveAsLilypondPDF(score, openPDF = False): |
|||
tempfile = os.path.join(gettempdir(), str(id(score)) + ".ly") |
|||
exportedLilypond = saveAsLilypond(score, tempfile) |
|||
ret = subprocess.call("lilypond --output={} {}".format(exportedLilypond, exportedLilypond), shell=True) #suffix to output added by lilypond |
|||
if ret == 0 and openPDF: |
|||
subprocess.Popen("xdg-open {}.pdf".format(exportedLilypond), shell=True) |
|||
|
|||
def stringToCharsOnlyString(string): |
|||
def num2word(n): |
|||
"""Take a number, return a str. |
|||
Lilypond does not allow numbers in variable names""" |
|||
return ''.join(chr(ord(c)+17) for c in str(n)) |
|||
|
|||
def num2wordForIterations(stringOrNum): |
|||
if stringOrNum in [1,2,3,4,5,6,7,8,9,0] or stringOrNum in "1234567890": |
|||
return num2word(int(stringOrNum)) |
|||
elif stringOrNum in ("-", "_"): |
|||
return "" |
|||
else: |
|||
return stringOrNum |
|||
|
|||
string = string.replace(" ", "_") |
|||
return "".join([num2wordForIterations(c) for c in string]) |
|||
|
|||
def lilyfy(string): |
|||
return string |
|||
|
|||
|
|||
def fromTemplate(session, templateFile, data, meta, tempoStaff): |
|||
"""Returns a string built from already ly-exported track data and a lilypond template. |
|||
Called by score.lilypond(), which is called by session.lilypond(). |
|||
A template is either one of the built-ins "foo.ly" or |
|||
if the templateFile is None/"" the current filename+ly is used: "bar.lbj.ly" |
|||
|
|||
Laborejo Markers in the template have the syntax %$$DATE$$ . That is pretty unique and additionaly a lilypond |
|||
comment. |
|||
""" |
|||
#Load the template |
|||
templatePath = findTemplate(session, templateFile) |
|||
with open(templatePath, 'r') as f: |
|||
templateString = f.read() |
|||
|
|||
templateString = templateString.replace("%$$DATE$$", da.today().strftime("%A %d. %B %Y")) #The current date |
|||
templateString = templateString.replace("%$$FILENAME$$", session.absoluteFilePath) |
|||
templateString = templateString.replace("%$$HEADER$$", processMeta(meta)) |
|||
templateString = templateString.replace("%$$SUBTEXT$$", lilyfy(meta["subtext"])) |
|||
voicesString, structureString = processData(data) |
|||
templateString = templateString.replace("%$$VOICES$$", voicesString) |
|||
templateString = templateString.replace("%$$STRUCTURE$$", structureString) |
|||
templateString = templateString.replace("%$$TEMPOSTAFF$$", tempoStaff) |
|||
|
|||
return templateString |
|||
|
|||
def findTemplate(session, templateFile): |
|||
"""returns a path. checks for existence. |
|||
see fromTemplate""" |
|||
if not templateFile: |
|||
path = session.absoluteFilePath, ".template.ly" |
|||
assert path.endswith(".lbj.ly") |
|||
else: |
|||
path = os.path.join(session.applicationDirectory, "laborejomodule", "templates", templateFile) |
|||
assert path.endswith(".ly") |
|||
|
|||
if os.path.exists(path): |
|||
return path |
|||
else: |
|||
raise FileNotFoundError(templateFile) |
|||
|
|||
def processData(data): |
|||
"""returns two strings. the first actual music data, VOICES, the second the structure and order, |
|||
STRUCTURE""" |
|||
def voice(track, lilypondTrack): |
|||
""" tgliHJuGCFAIICICBEHBJABHIJE= { \key c \major <c'>4 <e'>4 <g'>4 <c''>4 \bar "|."} """ |
|||
return "{} = {}".format(stringToCharsOnlyString(track.name), "{ " + lilypondTrack + " }") |
|||
|
|||
def structure(track, lilypondTrack): |
|||
""" \new Staff = "tgliHJuGCFAIICICBEHBJABHIJE_Staff" << \\new Voice = "tgliHJuGCFAIICICBEHBJABHIJE" \tgliHJuGCFAIICICBEHBJABHIJE >> %EndTrack """ |
|||
name = stringToCharsOnlyString(track.name) |
|||
instruments = '\\with {{ \n instrumentName = #"{}" \n shortInstrumentName = #"{}" }}'.format(lilyfy(track.initialInstrumentName), lilyfy(track.initialShortInstrumentName)) |
|||
mergeTempo = "\\new Voice = \"Tempo\" \\tempoStaff" |
|||
return """\\new Staff = "{}_Staff" {} << {} \\new Voice = "{}" \{} >> %EndTrack""".format(name, instruments, mergeTempo, name, name) |
|||
|
|||
voices = "\n ".join(voice(track, lilypondTrack) for track, lilypondTrack in data.items() if lilypondTrack) |
|||
structure = "\n ".join(structure(track, lilypondTrack) for track, lilypondTrack in data.items() if lilypondTrack) |
|||
|
|||
return voices, structure |
|||
|
|||
|
|||
def processMeta(meta): |
|||
"""returns a string with lilypond header data like title and composer""" |
|||
|
|||
def valueMarkup(value): |
|||
value = value.strip() |
|||
if value: |
|||
if value.startswith("\\markup"): |
|||
return value |
|||
else: |
|||
return "\"{}\"".format(value) #results in "value" |
|||
else: |
|||
return "##f" |
|||
|
|||
metaString = "\n ".join(key + " = " + valueMarkup(value) for key, value in meta.items()) |
|||
|
|||
return metaString |
@ -0,0 +1,52 @@ |
|||
|
|||
|
|||
class MidiState(object): |
|||
"""Various data to keep track what midi in looks like currently""" |
|||
def __init__(self): |
|||
self.midiInIsActive = False |
|||
self.ccState = {} |
|||
for cc in range(0,127): |
|||
self.ccState[cc] = None #start with undefined. |
|||
self.firstActiveNote = None #for chord entry. |
|||
self.cboxMidiPortUid = None #the INPUT midi in port. set in startMidiModule |
|||
self.currentOutputMidiPortUid = None #set in self.setMidiThrough. This changes with the active track |
|||
self.scene = cbox.Document.get_engine().new_scene() |
|||
self.scene.clear() |
|||
|
|||
def toggleMidiIn(self): |
|||
self.midiInIsActive = not self.midiInIsActive |
|||
if self.midiInIsActive: |
|||
#CachedTrackDuration is a dict and never cleared completely. So our midiIn will never get touched |
|||
#We hope that nobody uses the PortUid as a track name by chance :) |
|||
api.session.data.cachedTrackDurations[self.cboxMidiPortUid] = api.DINF |
|||
else: |
|||
api.session.data.cachedTrackDurations[self.cboxMidiPortUid] = 0 |
|||
|
|||
finalizeRecordingBuffer() |
|||
api.callbacksDatabase._prevailingBaseDurationChanged(api.session.data.cursor.prevailingBaseDuration) |
|||
|
|||
def updateCCState(self, ccNumber, ccValue): |
|||
assert 0 <= ccValue <= 127 |
|||
self.ccState[ccNumber] = ccValue |
|||
|
|||
def setMidiThrough(self, cursorExport): |
|||
""" |
|||
Instruct the RT part to echo midi in directly to the connect output ports so we hear the current track with the tracks instrument. |
|||
Added to the cursorChanged api callback""" |
|||
if not self.currentOutputMidiPortUid == cursorExport["cboxMidiOutUuid"]: #most of the time this stays the same e.g. cursor left/right. we only care about up and down |
|||
self.currentOutputMidiPortUid = cursorExport["cboxMidiOutUuid"] |
|||
cbox.JackIO.route_midi_input(midiState.cboxMidiPortUid, cursorExport["cboxMidiOutUuid"]) |
|||
|
|||
def startMidiModule(pApi): |
|||
global api |
|||
api = pApi |
|||
|
|||
#global midiState |
|||
#midiState = MidiState() |
|||
|
|||
midiState.cboxMidiPortUid = cbox.JackIO.create_midi_input("in") |
|||
cbox.JackIO.set_appsink_for_midi_input(midiState.cboxMidiPortUid, True) #This sounds like a program wide sink, but it is needed for every port. |
|||
cbox.JackIO.route_midi_input(midiState.cboxMidiPortUid, midiState.scene.uuid) |
|||
#midiState.currentOutputMidiPortUid = api.session.data.cursorExport()["cboxMidiOutUuid"] #TODO. Im cursor habn ich das deaktiviert. |
|||
#api.callbacks.setCursor.append(midiState.setMidiThrough) #TODO. Das brauchen wir damit wir das verknüpfte instrument vom track hören. |
|||
return processMidiIn |
@ -0,0 +1 @@ |
|||
Licenses for 3rd party resources can be found in text files with the same name as the resource. |
@ -0,0 +1,105 @@ |
|||
%% LilyPond-File generated by Laborejo - http://www.laborejo.org |
|||
%% Generation Date: %$$DATE$$ |
|||
%% Filename: %$$FILENAME$$ |
|||
|
|||
\version "2.18" |
|||
#(set-default-paper-size "a4") |
|||
#(set-global-staff-size 18 ) |
|||
#(ly:set-option 'point-and-click #f) |
|||
|
|||
\paper { |
|||
print-all-headers = ##f |
|||
%scoreTitleMarkup = ##f |
|||
|
|||
%Print small title on every page. |
|||
oddHeaderMarkup = \markup |
|||
\fill-line { |
|||
\on-the-fly #not-first-page \fromproperty #'header:title |
|||
\on-the-fly #not-first-page \fromproperty #'header:instrument |
|||
%"" % This will make sure that the header is never completely empty, to |
|||
% avoid some layout problems. Also, moving it in between the title and |
|||
% the page number, makes these be typeset left and right aligned, respectively. |
|||
\on-the-fly #not-first-page \on-the-fly #print-page-number-check-first \fromproperty #'page:page-number-string |
|||
} |
|||
|
|||
evenHeaderMarkup = \markup |
|||
\fill-line { |
|||
\on-the-fly #print-page-number-check-first \fromproperty #'page:page-number-string |
|||
\fromproperty #'header:instrument |
|||
\fromproperty #'header:title |
|||
} |
|||
|
|||
} |
|||
|
|||
\header{ |
|||
%$$HEADER$$ |
|||
} |
|||
|
|||
correctFraction = #(lambda (grob) |
|||
(let ((ev (event-cause grob))) |
|||
(if (and (= (ly:event-property ev 'numerator) 2) (= (ly:event-property ev 'denominator) 3)) |
|||
|
|||
(format #f "~a" 3) |
|||
|
|||
(format #f "~a/~a" |
|||
(ly:event-property ev 'numerator) |
|||
(ly:event-property ev 'denominator)) |
|||
))) |
|||
|
|||
|
|||
\layout{ |
|||
%ragged-right = ##t |
|||
\context { \Score |
|||
\override TupletNumber #'text = \correctFraction %traditional but wrong: #tuplet-number::calc-fraction-text |
|||
\override Fingering #'add-stem-support = ##t |
|||
\override StringNumber #'add-stem-support = ##t |
|||
\override StrokeFinger #'add-stem-support = ##t |
|||
%Dynamics |
|||
\override Hairpin #'to-barline = ##f |
|||
\override DynamicLineSpanner #'staff-padding = #2.0 |
|||
\override DynamicLineSpanner #'Y-extent = #'(-1.5 . 1.5) |
|||
\override TextScript #'Y-extent = #'(-1.5 . 1.5) |
|||
\override PaperColumn #'keep-inside-line = ##t |
|||
%Remove engraver here and add them in context staff to make multiple timesigs possible. This deactivates barnumbers! |
|||
%\remove "Timing_translator" |
|||
%\remove "Default_bar_line_engraver" |
|||
%\override Tie #'details #'ratio = #2 |
|||
} |
|||
\context { \Voice |
|||
%Exchange note_heads_engraver with the completition variant which auto-ties notes on bar-breaks |
|||
\remove "Note_heads_engraver" |
|||
\consists "Completion_heads_engraver" |
|||
tieWaitForNote = ##t |
|||
} |
|||
\context { \Staff |
|||
\RemoveEmptyStaves |
|||
%\override VerticalAxisGroup #'remove-first = ##t |
|||
\override TimeSignature #'style = #'numbered |
|||
|
|||
%Commenting the following two lines in again, together with the revmoval in \score enables mutli-timesig mode. But disables barnumbers. |
|||
%\consists "Timing_translator" |
|||
%\consists "Default_bar_line_engraver" |
|||
|
|||
\override NoteHead #'style = #'baroque %breve and longa are squared. |
|||
} |
|||
} |
|||
|
|||
|
|||
%Music definitions. Containers, Staff, Parts. |
|||
tempoStaff = { %$$TEMPOSTAFF$$ } |
|||
|
|||
%$$VOICES$$ |
|||
|
|||
\score{ |
|||
<< |
|||
%How the definitions are arranged. Only staffgroups (staff prefix) and staffs, merged as voices. |
|||
\accidentalStyle Score.neo-modern-voice |
|||
%$$STRUCTURE$$ |
|||
>>} |
|||
|
|||
\markup{ |
|||
%\column { |
|||
%$$SUBTEXT$$ |
|||
%} |
|||
} |
|||
|
@ -0,0 +1,17 @@ |
|||
%% LilyPond-File generated by Laborejo - http://www.laborejo.org |
|||
%% Generation Date: %$$DATE$$ |
|||
%% Filename: %$$FILENAME$$ |
|||
|
|||
\version "2.18" |
|||
#(ly:set-option 'point-and-click #f) |
|||
|
|||
%Music definitions. Containers, Staff, Parts. |
|||
%$$VOICES$$ |
|||
|
|||
\score {<< |
|||
%How the definitions are arranged. Only staffgroups (staff prefix) and staffs, merged as voices. |
|||
%$$STRUCTURE$$ |
|||
>>} |
|||
|
|||
%no subtext |
|||
|
@ -0,0 +1,9 @@ |
|||
#! /usr/bin/env python3 |
|||
# -*- coding: utf-8 -*- |
|||
|
|||
from template import start #Executes various start up checks and sets up our environment likes search paths |
|||
|
|||
from qtgui import mainwindow #which in turn imports the engine and starts the engine |
|||
with start.profiler(): |
|||
mainwindow.MainWindow().qtApp.exec_() |
|||
#Program is over. Code here does not get executed. Quit is done via NSM in mainWindow._nsmQuit |
@ -0,0 +1,103 @@ |
|||
#! /usr/bin/env python3 |
|||
# -*- coding: utf-8 -*- |
|||
""" |
|||
Copyright 2019, Nils Hilbricht, Germany ( https://www.hilbricht.net ) |
|||
|
|||
This file is part of the Laborejo Software Suite ( https://www.laborejo.org ), |
|||
more specifically its template base application. |
|||
|
|||
The Template Base Application is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
""" |
|||
|
|||
import logging; logging.info("import {}".format(__file__)) |
|||
|
|||
#Python Standard Library |
|||
|
|||
#Third Party Modules |
|||
|
|||
#Template Modules |
|||
from template.engine.input_midi import MidiInput |
|||
|
|||
#Our Modules |
|||
import engine.api as api |
|||
|
|||
|
|||
class StepMidiInput(MidiInput): |
|||
"""We initialize with everything switched off. This makes it easier for a complex system |
|||
of engine, GUI and midiinput to start up and get the order of operations right |
|||
|
|||
For chord entry we use the first note only as indicator. The indicator was a set() at one time, |
|||
which means that as long as one of the chord notes would still be down the chord was still "on". |
|||
That is not as robust and convenient as using the starting note, which is counter intuitive, |
|||
therefore documented here. |
|||
""" |
|||
|
|||
def __init__(self): |
|||
self.firstActiveNote = None #for chord entry. |
|||
self._currentlyActiveNotes = set() |
|||
|
|||
def start(self): |
|||
"""Call this manually after the engine and an event loop have started. |
|||
For example from the GUI. It is currently started by mainwindow.py start() |
|||
But it could be started from a simple command line interface as well.""" |
|||
assert api.laborejoEngineStarted |
|||
super().__init__(session=api.session, portName="in") |
|||
self.midiProcessor.active = False #specific to Laborejo |
|||
|
|||
#Connect the template midi input with Laborejo api calls. |
|||
#self.midiProcessor.notePrinter(True) |
|||
self.midiProcessor.register_NoteOn(self._insertMusicItemFromMidi) |
|||
self.midiProcessor.register_NoteOff(self._pop) |
|||
|
|||
api.callbacks.setCursor.append(self._setMidiThru) #When the track changes re-route cbox RT midi thru |
|||
|
|||
@property |
|||
def midiInIsActive(self): |
|||
try: |
|||
return self.midiProcessor.active |
|||
except AttributeError: #during startupt |
|||
return False |
|||
|
|||
def _insertMusicItemFromMidi(self, channel, midipitch, velocity): |
|||
if self._currentlyActiveNotes: #Chord |
|||
api.left() |
|||
keysig = api.session.data.currentTrack().state.keySignature() |
|||
pitchToInsert = api.pitchmath.fromMidi(midipitch, keysig) |
|||
api.addNoteToChord(pitchToInsert) |
|||
api.right() |
|||
else: #Single note |
|||
baseDuration = api.session.data.cursor.prevailingBaseDuration |
|||
keysig = api.session.data.currentTrack().state.keySignature() |
|||
pitchToInsert = api.pitchmath.fromMidi(midipitch, keysig) |
|||
api.insertChord(baseDuration, pitchToInsert) |
|||
|
|||
self._currentlyActiveNotes.add(midipitch) |
|||
|
|||
def _pop(self, channel, midipitch, velocity): |
|||
self._currentlyActiveNotes.remove(midipitch) |
|||
|
|||
def setMidiInputActive(self, state:bool): |
|||
self.midiProcessor.active = state |
|||
api.callbacks._prevailingBaseDurationChanged(api.session.data.cursor.prevailingBaseDuration) |
|||
|
|||
def toggleMidiIn(self): |
|||
self.setMidiInputActive(not self.midiInIsActive) |
|||
|
|||
def _setMidiThru(self, cursorExport): |
|||
"""We don't need to react to deleted tracks because that does reset the cursor. |
|||
The template midi in does _not_ check if the routed output ports still exist. |
|||
however, that is a low risk state that needs changes in the program""" |
|||
self.setMidiThru(cursorExport["cboxMidiOutUuid"]) |
|||
|
|||
stepMidiInput = StepMidiInput() #global to use in other parts of Laborejo |
@ -0,0 +1,635 @@ |
|||
#! /usr/bin/env python3 |
|||
# -*- coding: utf-8 -*- |
|||
""" |
|||
Copyright 2017, Nils Hilbricht, Germany ( https://www.hilbricht.net ) |
|||
|
|||
This file is part of Laborejo ( https://www.laborejo.org ) |
|||
|
|||
Laborejo is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
""" |
|||
|
|||
import logging; logging.info("import {}".format(__file__)) |
|||
|
|||
from PyQt5 import QtCore, QtGui, QtSvg, QtWidgets |
|||
from .constantsAndConfigs import constantsAndConfigs |
|||
from template.qtgui.helper import stringToColor, removeInstancesFromScene, callContextMenu, stretchLine, stretchRect |
|||
from template.helper import pairwise |
|||
from .submenus import SecondaryTempoChangeMenu, TempoBlockPropertiesEdit |
|||
import engine.api as api |
|||
|
|||
oneRectToReturnThemAll = QtCore.QRectF(0,0,0,0) #prevent the annoying "NotImplementError" from Qt for boundingRect. For items that don't need any collision detection. |
|||
|
|||
_zValuesRelativeToConductor = { #Only use for objects added directly to the Conductor, not their children. |
|||
"line":0, |
|||
"block":3, |
|||
"item":4, |
|||
"handle":5, |
|||
} |
|||
|
|||
class Conductor(QtWidgets.QGraphicsItem): |
|||
"""The track for tempo items. Some methods have the same name and functionality |
|||
as note-track to be compatible. For example drag and drop of blocks.""" |
|||
|
|||
def __init__(self, parentView): |
|||
super().__init__() |
|||
|
|||
self.parentView = parentView |
|||
self.totalHeight = 71 |
|||
|
|||
self.staticPoints = None #Cached backend staticRepresentationList: TempoPoints and interpolated points list |
|||
self.staticBlocks = None #Cached Block Data list |
|||
self.staticMeta = None #Cached track meta data dict. |
|||
|
|||
self.staffLine = QtWidgets.QGraphicsLineItem(0,0,10,0) #x1, y1, x2, y2 |
|||
self.staffLine.setParentItem(self) |
|||
self.staffLine.setPos(0,0) |
|||
|
|||
self.timeLine = TimeLine(self) #registers its own callbacks |
|||
self.timeLine.setParentItem(self) |
|||
|
|||
api.callbacks.updateTempoTrackBlocks.append(self.updateBlocks) |
|||
api.callbacks.updateTempoTrack.append(self.createGraphicItemsFromData) |
|||
api.callbacks.updateTempoTrackMeta.append(self.updateMetaData) |
|||
|
|||
def paint(self, *args): |
|||
pass |
|||
def boundingRect(self, *args): |
|||
return oneRectToReturnThemAll |
|||
|
|||
def blockAt(self, xScenePosition): |
|||
for block in ConductorTransparentBlock.instances: |
|||
start = block.staticExportItem["position"] / constantsAndConfigs.ticksToPixelRatio |
|||
end = start + block.staticExportItem["duration"] / constantsAndConfigs.ticksToPixelRatio |
|||
if start <= xScenePosition < end: |
|||
return block |
|||
return None #After the last block. |
|||
|
|||
@property |
|||
def transparentBlockHandles(self): |
|||
return ConductorTransparentBlock.instances |
|||
|
|||
def updateMetaData(self, trackMetaDictionary): |
|||
"""Keep the meta data up to date. |
|||
Meta Data is (absolute) min and max tempo values: |
|||
{'minimumAbsoluteTempoValue': 120, 'maximumAbsoluteTempoValue': 120} |
|||
""" |
|||
self.staticMeta = trackMetaDictionary |
|||
|
|||
def updateBlocks(self, staticRepresentationList): |
|||
"""This is called when the blocks itself change, of course. |
|||
But also """ |
|||
self.staticBlocks = staticRepresentationList |
|||
|
|||
removeInstancesFromScene(ConductorTransparentBlock) |
|||
for dictExportItem in staticRepresentationList: |
|||
guiBlock = ConductorTransparentBlock(parent = self, staticExportItem = dictExportItem, x = 0, y = -10, w = dictExportItem["duration"] / constantsAndConfigs.ticksToPixelRatio, h = 20) |
|||
guiBlock.setParentItem(self) |
|||
guiBlock.setPos(dictExportItem["position"] / constantsAndConfigs.ticksToPixelRatio,0) |
|||
guiBlock.setZValue(_zValuesRelativeToConductor["block"]) #including the handle |
|||
|
|||
rightBorder = (dictExportItem["duration"] + dictExportItem["position"]) / constantsAndConfigs.ticksToPixelRatio |
|||
self.updateStaffLine(rightBorder) |
|||
|
|||
def blockById(self, backendId): |
|||
for guiblock in ConductorTransparentBlock.instances: |
|||
if guiblock.staticExportItem["id"] == backendId: |
|||
return guiblock |
|||
#else: |
|||
# raise ValueError(f"{backendId} not found") |
|||
|
|||
|
|||
def updateStaffLine(self, x): |
|||
assert not self.staffLine.line().isNull() |
|||
line = self.staffLine.line() |
|||
line.setLength(x) |
|||
self.staffLine.setLine(line) |
|||
self.staffLine.setZValue(_zValuesRelativeToConductor["line"]) |
|||
|
|||
def createGraphicItemsFromData(self, staticRepresentationList): |
|||
self.staticPoints = staticRepresentationList |
|||
|
|||
removeInstancesFromScene(TempoPoint) |
|||
y = -35 #The Y Value adjusts for the offset the text-item creates |
|||
|
|||
for point in staticRepresentationList: |
|||
if not point["type"] == "interpolated": #a real user point or lastInBlock or lastInTrack |
|||
x = point["position"] / constantsAndConfigs.ticksToPixelRatio |
|||
p = TempoPoint(self, point, self.blockById(point["blockId"])) |
|||
p.setParentItem(self) |
|||
p.setPos(x, y) |
|||
|
|||
|
|||
def stretchXCoordinates(self, factor): |
|||
"""Reposition the items on the X axis. |
|||
Call goes through all parents/children, starting from ScoreView._stretchXCoordinates. |
|||
Docstring there.""" |
|||
|
|||
positionalItems = (TempoPoint.instances + ConductorTransparentBlock.instances) |
|||
|
|||
for tempoPoint in positionalItems: |
|||
tempoPoint.setX(tempoPoint.pos().x() * factor) |
|||
|
|||
for block in ConductorTransparentBlock.instances: |
|||
block.stretchXCoordinates(factor) |
|||
|
|||
stretchLine(self.staffLine, factor) |
|||
self.timeLine.stretchXCoordinates(factor) |
|||
|
|||
|
|||
def mousePressEvent(self, event): |
|||
"""It is possible that this has coordinates outside of the Conductor instance. When the |
|||
mousePressEvent is inside and the mouse moves outside for the release event it still counts |
|||
as event of this instance""" |
|||
|
|||
if event.button() == 1 and 0 <= event.scenePos().x() < self.staffLine.line().x2(): #within the conductor line: # QtCore.Qt.MouseButton.LeftButton |
|||
event.accept() |
|||
self.add(event.scenePos()) #create a new tempo point by telling the api a position and then reacting to "delete all, recreate" from the callback. |
|||
else: |
|||
super().mousePressEvent(event) #call default implementation from QGraphicsRectItem |
|||
|
|||
def add(self, scenePos): |
|||
"""Use a scenePos (from self.mousePressEvent) to instruct the backend |
|||
to create a new tempo point.""" |
|||
unitsPerMinute = 120 |
|||
referenceTicks = api.D4 |
|||
|
|||
sp = scenePos.x() * constantsAndConfigs.ticksToPixelRatio |
|||
if constantsAndConfigs.snapToGrid: |
|||
sp = round(sp / constantsAndConfigs.gridRhythm) * constantsAndConfigs.gridRhythm |
|||
|
|||
api.insertTempoItemAtAbsolutePosition(sp, unitsPerMinute, referenceTicks, graphType = "standalone") |
|||
|
|||
class ConductorTransparentBlock(QtWidgets.QGraphicsRectItem): |
|||
"""A simplified version of a Block. Since we don't use blocks in the GUI, only in the backend |
|||
we still need them sometimes as macro strutures, where we don't care about the content. |
|||
|
|||
The block handle is at the END of a block. """ |
|||
|
|||
|
|||
class ConductorBlockName(QtWidgets.QGraphicsSimpleTextItem): |
|||
|
|||
instances = [] |
|||
|
|||
def __init__(self, parent, positionInSeconds): |
|||
self.__class__.instances.append(self) |
|||
m, s = divmod(positionInSeconds, 60) |
|||
text = "{}:{} min".format(str(int(m)).zfill(2), str(int(s)).zfill(2)) |
|||
super().__init__(text) |
|||
|
|||
marker = QtWidgets.QGraphicsLineItem(0, 0, 0, -10) #vertical marker to connect to the conductor line |
|||
marker.setParentItem(self) |
|||
|
|||
|
|||
instances = [] |
|||
|
|||
def __init__(self, parent, staticExportItem, x, y, w, h): |
|||
self.__class__.instances.append(self) |
|||
super().__init__(x, y, w, h) |
|||
self.setFlags(QtWidgets.QGraphicsItem.ItemDoesntPropagateOpacityToChildren|QtWidgets.QGraphicsItem.ItemIsMovable) #no mouseReleaseEvent without selection or movable. |
|||
self.parent = parent #Conductor instance |
|||
self.color = stringToColor(staticExportItem["name"]) |
|||
self.trans = QtGui.QColor("transparent") |
|||
self.setPen(self.trans) |
|||
self.setBrush(self.color) |
|||
self.setOpacity(0.2) #mimic background behaviour |
|||
self.staticExportItem = staticExportItem |
|||
|
|||
self.posBeforeMove = None |
|||
self.cursorPosOnMoveStart = None |
|||
|
|||
#Display Block ID |
|||
#self.idText = QtWidgets.QGraphicsSimpleTextItem(str(self.staticExportItem["id"])) |
|||
#self.idText.setParentItem(self) |
|||
#self.idText.setPos(0, -30) |
|||
|
|||
if self.staticExportItem["duration"] >= 8*api.D1: #cosmetics |
|||
self.startLabel = QtWidgets.QGraphicsSimpleTextItem(self.staticExportItem["name"] + " start") |
|||
self.startLabel.setParentItem(self) |
|||
self.startLabel.setPos(15, -2*constantsAndConfigs.stafflineGap) |
|||
self.startLabel.setFlags(QtWidgets.QGraphicsItem.ItemIgnoresParentOpacity) |
|||
|
|||
self.endLabel = QtWidgets.QGraphicsSimpleTextItem(self.staticExportItem["name"] + " end ") |
|||
self.endLabel.setParentItem(self) |
|||
self.endLabel.setPos(self.rect().width() - self.endLabel.boundingRect().width(), -2*constantsAndConfigs.stafflineGap) |
|||
self.endLabel.setFlags(QtWidgets.QGraphicsItem.ItemIgnoresParentOpacity) |
|||
else: |
|||
self.startLabel = QtWidgets.QGraphicsSimpleTextItem("") |
|||
self.endLabel = QtWidgets.QGraphicsSimpleTextItem("") |
|||
|
|||
#Add End Line |
|||
self.marker = ConductorBlockHandle(parent = self) |
|||
self.marker.setParentItem(self) |
|||
self.marker.setPos(staticExportItem["duration"] / constantsAndConfigs.ticksToPixelRatio, -1/2* self.rect().height()+2) |
|||
|
|||
#def paint(self, *args): |
|||
# """Prevent the selection rectangle when clicking the item""" |
|||
#!! This also prevents the rectangle to show up. Very bad decision. |
|||
# pass |
|||
|
|||
def stretchXCoordinates(self, factor): |
|||
"""Reposition the items on the X axis. |
|||
Call goes through all parents/children, starting from ScoreView._stretchXCoordinates. |
|||
Docstring there.""" |
|||
stretchRect(self, factor) |
|||
self.marker.setX(self.marker.pos().x() * factor) |
|||
|
|||
def mouseMoveEvent(self, event): |
|||
"""Don't use the qt system. we move ourselves""" |
|||
event.accept() |
|||
|
|||
def mousePressEvent(self, event): |
|||
self.parent.mousePressEvent(event) |
|||
|
|||
def mouseMoveEventCustom(self, event): |
|||
""" |
|||
Move the whole block, change the tempoTrack form. |
|||
Custom gets called by the scene mouse press event directly only when the right keys |
|||
are hold down""" |
|||
# All the positions below don't work. They work fine when dragging Tracks around but not this Item. I can't be bothered to figure out why. |
|||
#scenePos() results ins an item position that is translated down and right. The higher the x/y value the more the offset |
|||
#Instead we calculate our delta ourselves. |
|||
|
|||
#self.setPos(self.mapToItem(self, event.scenePos())) |
|||
#self.setPos(self.mapFromScene(event.scenePos())) |
|||
#posGlobal = QtGui.QCursor.pos() |
|||
#posView = self.parent.parentScore.parentView.mapFromGlobal(posGlobal) #a widget |
|||
#posScene = self.parent.parentScore.parentView.mapToScene(posView) |
|||
#print (posGlobal, posView, posScene, event.scenePos()) |
|||
|
|||
if self.cursorPosOnMoveStart: |
|||
self.setPos(event.scenePos().x(), self.posBeforeMove.y()) |
|||
|
|||
""" |
|||
#does not work with zooming |
|||
if self.cursorPosOnMoveStart: |
|||
delta = QtGui.QCursor.pos() - self.cursorPosOnMoveStart |
|||
new = self.posBeforeMove + delta |
|||
if new.x() < 0: |
|||
self.setPos(0, self.posBeforeMove.y()) |
|||
else: |
|||
self.setPos(new.x(), self.posBeforeMove.y()) |
|||
#event.ignore() #this blocks the qt movable object since we already move the object on our own. |
|||
""" |
|||
super().mouseMoveEvent(event) |
|||
|
|||
def mousePressEventCustom(self, event): |
|||
"""Custom gets called by the scene mouse press event directly only when the right keys |
|||
are hold down""" |
|||
self.posBeforeMove = self.pos() |
|||
self.cursorPosOnMoveStart = QtGui.QCursor.pos() |
|||
#self.setBrush(self.color) |
|||
super().mousePressEvent(event) |
|||
|
|||
def mouseReleaseEventCustom(self, event): |
|||
"""Custom gets called by the scene mouse press event directly only when the right keys |
|||
are hold down""" |
|||
#self.setBrush(self.trans) |
|||
self.setPos(self.posBeforeMove) #In case the block was moved to a position where no track is (below the tracks) we just reset the graphics. If the moving was correct then the new position will be set by redrawing the whole Conductor. |
|||
self.posBeforeMove = None |
|||
self.cursorPosOnMoveStart = None |
|||
super().mouseReleaseEvent(event) |
|||
|
|||
def splitHere(self, event): |
|||
posRelativeToBlockStart = event.scenePos().x() * constantsAndConfigs.ticksToPixelRatio - self.x() * constantsAndConfigs.ticksToPixelRatio |
|||
if constantsAndConfigs.snapToGrid: |
|||
posRelativeToBlockStart = round(posRelativeToBlockStart / constantsAndConfigs.gridRhythm) * constantsAndConfigs.gridRhythm |
|||
|
|||
if posRelativeToBlockStart > 0: |
|||
api.splitTempoBlock(self.staticExportItem["id"], int(posRelativeToBlockStart)) |
|||
|
|||
def contextMenuEvent(self, event): |
|||
listOfLabelsAndFunctions = [ |
|||
("edit properties", lambda: TempoBlockPropertiesEdit(self.scene().parentView.mainWindow, staticExportItem = self.staticExportItem)), |
|||
("separator", None), |
|||
("split here", lambda: self.splitHere(event)), |
|||
("duplicate", lambda: api.duplicateTempoBlock(self.staticExportItem["id"])), |
|||
("create content link", lambda: api.duplicateContentLinkTempoBlock(self.staticExportItem["id"])), |
|||
("unlink", lambda: api.unlinkTempoBlock(self.staticExportItem["id"])), |
|||
("separator", None), |
|||
("join with next block", lambda: api.mergeWithNextTempoBlock(self.staticExportItem["id"])), |
|||
("delete block", lambda: api.deleteTempoBlock(self.staticExportItem["id"])), |
|||
("separator", None), |
|||
("append block at the end", api.appendTempoBlock), |
|||
] |
|||
callContextMenu(listOfLabelsAndFunctions) |
|||
|
|||
class ConductorBlockHandle(QtWidgets.QGraphicsLineItem): |
|||
"""Provides user interaction so the temp block can be resized by moving this handle with the |
|||
mouse left and right. |
|||
When user interaction happens this handle acts upon its parent transparent block to resize it |
|||
and finally sends a message to the backend, to ask for a data change.""" |
|||
|
|||
def __init__(self, parent): |
|||
self.parentTransparentBlock = parent |
|||
super().__init__(0,-1, 0, parent.rect().height()-4) #x1, y1, x2, y2 |
|||
self.setFlags(QtWidgets.QGraphicsItem.ItemIsMovable|QtWidgets.QGraphicsItem.ItemSendsGeometryChanges|QtWidgets.QGraphicsItem.ItemIsFocusable|QtWidgets.QGraphicsItem.ItemClipsToShape) |
|||
self.setAcceptHoverEvents(True) |
|||
self.setCursor(QtCore.Qt.SizeHorCursor) |
|||
|
|||
pen = QtGui.QPen() # creates a default pen |
|||
if not self.parentTransparentBlock.staticExportItem["exportsAllItems"]: |
|||
pen.setStyle(QtCore.Qt.DotLine) |
|||
pen.setWidth(2) |
|||
self.setPen(pen) |
|||
|
|||
self.inactivePen = pen |
|||
self.inactivePen.setColor(QtGui.QColor("black")) |
|||
|
|||
self.activePen = QtGui.QPen(pen) |
|||
self.activePen.setColor(QtGui.QColor("cyan")) |
|||
|
|||
self.minimalSize = api.D1 / constantsAndConfigs.ticksToPixelRatio |
|||
|
|||
self.setZValue(_zValuesRelativeToConductor["handle"]) |
|||
|
|||
def shape(self): |
|||
"""Return a more accurate shape for this item so that |
|||
mouse hovering is more accurate""" |
|||
path = QtGui.QPainterPath() |
|||
path.addRect(QtCore.QRectF(-2, -2, 5, self.parentTransparentBlock.rect().height()+2 )) #this is directly related to inits parameter x, y, w, h |
|||
return path |
|||
|
|||
def hoverEnterEvent(self, event): |
|||
self.setPen(self.activePen) |
|||
#self.parentTransparentBlock.setBrush(self.parentTransparentBlock.color) |
|||
|
|||
|
|||
def hoverLeaveEvent(self, event): |
|||
self.setPen(self.inactivePen) |
|||
#self.parentTransparentBlock.setBrush(self.parentTransparentBlock.trans) |
|||
|
|||
def mousePressEvent(self, event): |
|||
self.posBeforeMove = self.pos() |
|||
self.cursorPosOnMoveStart = QtGui.QCursor.pos() |
|||
super().mousePressEvent(event) |
|||
|
|||
def mouseMoveEvent(self, event): |
|||
if not self.cursorPosOnMoveStart: |
|||
super().mouseMoveEvent(event) |
|||
return None |
|||
|
|||
delta = QtGui.QCursor.pos() - self.cursorPosOnMoveStart |
|||
new = self.posBeforeMove + delta |
|||
|
|||
if not new.x() < self.minimalSize: |
|||
self.setPos(new.x(), self.posBeforeMove.y()) |
|||
pRect = self.parentTransparentBlock.rect() |
|||
pRect.setRight(new.x()) |
|||
self.parentTransparentBlock.setRect(pRect) |
|||
|
|||
event.accept() #this blocks the qt movable object since we already move the object on our own. |
|||
#Don't call the super mouseMoveEvent! |
|||
|
|||
def mouseReleaseEvent(self, event): |
|||
if self.cursorPosOnMoveStart: |
|||
|
|||
endingRelativeToBlockStart = self.x() * constantsAndConfigs.ticksToPixelRatio - self.parentTransparentBlock.x() |
|||
if constantsAndConfigs.snapToGrid: |
|||
endingRelativeToBlockStart = round(endingRelativeToBlockStart / constantsAndConfigs.gridRhythm) * constantsAndConfigs.gridRhythm |
|||
assert endingRelativeToBlockStart > 0 |
|||
|
|||
self.setPos(self.posBeforeMove) #In case the handle was moved to a position where it wasn't allowed no backend action will happen. Just in case we reset the graphics for a smoother user experience. |
|||
self.posBeforeMove = None |
|||
self.cursorPosOnMoveStart = None |
|||
|
|||
api.changeTempoBlockDuration(self.parentTransparentBlock.staticExportItem["id"], endingRelativeToBlockStart) |
|||
|
|||
super().mouseReleaseEvent(event) |
|||
|
|||
class TempoPoint(QtWidgets.QGraphicsItem): |
|||
"""A point where the values can be edited by the user. |
|||
|
|||
The first TempoPoint cannot be hovered. It is instead attached to a block handle. |
|||
""" |
|||
|
|||
instances = [] |
|||
|
|||
def __init__(self, parentTempoTrack, staticExportItem, parentBlock): |
|||
self.__class__.instances.append(self) |
|||
super().__init__() |
|||
self.staticExportItem = staticExportItem |
|||
self.parentTempoTrack = parentTempoTrack |
|||
self.parentBlock = parentBlock |
|||
self.setZValue(_zValuesRelativeToConductor["item"]) |
|||
|
|||
|
|||
self.setAcceptHoverEvents(True) |
|||
|
|||
|
|||
if not self.staticExportItem["positionInBlock"] == 0: |
|||
self.setFlags(QtWidgets.QGraphicsItem.ItemIsMovable|QtWidgets.QGraphicsItem.ItemIsFocusable) |
|||
#Too irritating. And confuses with handle movement. self.setCursor(QtCore.Qt.SizeHorCursor) #this sets the cursor while the mouse is over the item. It is independent of AcceptHoverEvents |
|||
else: |
|||
self.ungrabMouse = api.nothing #to surpress a warning from the context menu |
|||
|
|||
self.note = QtWidgets.QGraphicsTextItem("") |
|||
self.note.setParentItem(self) |
|||
self.note.setFont(constantsAndConfigs.musicFont) |
|||
self.note.setHtml("<font size='6'>{}</font>".format(constantsAndConfigs.realNoteDisplay[staticExportItem["referenceTicks"]])) |
|||
self.note.setPos(-6,0) #adjust items font x offsset. |
|||
|
|||
self.number = QtWidgets.QGraphicsTextItem("") |
|||
self.number.setParentItem(self) |
|||
#self.number.setHtml("<font color='black' size='2'>{}</font>".format(str(int(staticExportItem["unitsPerMinute"])))) |
|||
self.number.setHtml("<font size='2'>{}</font>".format(str(int(staticExportItem["unitsPerMinute"])))) |
|||
self.number.setPos(-6,0) #adjust items font x offsset. |
|||
|
|||
if not self.staticExportItem["graphType"] == "standalone": |
|||
self.arrow = QtWidgets.QGraphicsTextItem("⟶") #unicode long arrow right #http://xahlee.info/comp/unicode_arrows.html |
|||
self.arrow.setParentItem(self) |
|||
self.arrow.setPos(7,30) |
|||
else: |
|||
self.arrow = None |
|||
|
|||
for n in (self.note, self.number, self.arrow): |
|||
if n: n.setDefaultTextColor(QtGui.QColor("black")) |
|||
|
|||
self.wheelEventChangedValue = 0 #resetted in hoverEnterEvent. But we still need it for new items that appear directly under the mouse cursor |
|||
|
|||
def paint(self, painter, options, widget=None): |
|||
#painter.drawRect(self.boundingRect()) #uncomment to show the bounding rect |
|||
pass |
|||
|
|||
def boundingRect(self): |
|||
return QtCore.QRectF(0,0,25,50) #x, y, w, h |
|||
|
|||
|
|||
def mouseMoveEvent(self, event): |
|||
if self.staticExportItem["positionInBlock"] == 0: |
|||
#First in block can't be moved |
|||
event.accept() |
|||
return |
|||
|
|||
#toTheRight = True if event.scenePos().x() - event.lastScenePos().x()) > 0 else False |
|||
|
|||
delta = event.scenePos().x() - event.lastScenePos().x() |
|||
newPos = self.x() + delta |
|||
|
|||
if 0 <= newPos < self.parentTempoTrack.staffLine.line().x2(): #within the conductor line |
|||
self.setX(newPos) |
|||
event.accept() |
|||
|
|||
def mousePressEvent(self, event): |
|||
"""Override the mousePressEvent to deactivate it. |
|||
Otherwise the event will be sent to the parent block and create a new TempoItem |
|||
at this point even if there is already one. Effectively replacing a custom item with |
|||
default value""" |
|||
#if self.staticExportItem["positionInBlock"] == 0: |
|||
# print ("no") |
|||
# #super().mousePressEvent(event) |
|||
event.accept() # |
|||
|
|||
def mouseReleaseEvent(self, event): |
|||
if self.staticExportItem["positionInBlock"] == 0: |
|||
#First in block can't be moved |
|||
event.accept() |
|||
return |
|||
|
|||
tickPositionAbsolute = self.scenePos().x() * constantsAndConfigs.ticksToPixelRatio |
|||
|
|||
if constantsAndConfigs.snapToGrid: |
|||
tickPositionAbsolute = round(tickPositionAbsolute / constantsAndConfigs.gridRhythm) * constantsAndConfigs.gridRhythm |
|||
|
|||
api.moveTempoItem(self.staticExportItem["id"], tickPositionAbsolute) |
|||
event.accept() |
|||
|
|||
def hoverEnterEvent(self, event): |
|||
self.parentTempoTrack.parentView.mainWindow.ui.actionDelete.setEnabled(False) #Delete key collides with our hover-delete. |
|||
self.grabKeyboard() |
|||
self.wheelEventChangedValue = 0 |
|||
for n in (self.note, self.number, self.arrow): |
|||
if n: n.setDefaultTextColor(QtGui.QColor("cyan")) |
|||
|
|||
def hoverLeaveEvent(self, event): |
|||
self.parentTempoTrack.parentView.mainWindow.ui.actionDelete.setEnabled(True) #Delete key collides with our hover-delete. |
|||
self.ungrabKeyboard() |
|||
for n in (self.note, self.number, self.arrow): |
|||
if n: n.setDefaultTextColor(QtGui.QColor("black")) |
|||
if self.wheelEventChangedValue: |
|||
api.insertTempoItemAtAbsolutePosition(self.staticExportItem["position"], self.staticExportItem["unitsPerMinute"] + self.wheelEventChangedValue, self.staticExportItem["referenceTicks"], self.staticExportItem["graphType"]) |
|||
|
|||
def wheelEvent(self, event): |
|||
"""This buffers until hoverLeaveEvent and then the new value is sent in self.hoverLeaveEvent""" |
|||
if event.delta() > 0: |
|||
self.wheelEventChangedValue += 1 |
|||
else: |
|||
self.wheelEventChangedValue -= 1 |
|||
self.number.setHtml("<font size='2'>{}</font>".format(str(int(self.staticExportItem["unitsPerMinute"] + self.wheelEventChangedValue)))) |
|||
event.accept() |
|||
|
|||
def keyPressEvent(self, event): |
|||
"""Handle the delete item key. |
|||
Needs grabKeyboard, but NOT setFocus |
|||
|
|||
The event will not be sent if it is blocked by a global shortcut. |
|||
""" |
|||
key = event.key() |
|||
if key == 16777223: |
|||
#after delete the item and tempo tracks gets recreated so we need to reactivate the shortcut now. It will work without these two lines, but that is only implicit behaviour. |
|||
self.parentTempoTrack.parentView.mainWindow.ui.actionDelete.setEnabled(True) #Delete key collides with our hover-delete. |
|||
self.ungrabKeyboard() |
|||
api.removeTempoItem(self.staticExportItem["id"]) |
|||
else: |
|||
return super().keyPressEvent(event) |
|||
|
|||
def contextMenuEvent(self, event): |
|||
listOfLabelsAndFunctions = [ |
|||
("edit properties", lambda: SecondaryTempoChangeMenu(self.scene().parentView.mainWindow, staticExportTempoItem = self.staticExportItem)), |
|||
] |
|||
if not self.staticExportItem["positionInBlock"] == 0: |
|||
listOfLabelsAndFunctions.append(("delete", lambda: api.removeTempoItem(self.staticExportItem["id"]))) |
|||
callContextMenu(listOfLabelsAndFunctions) |
|||
|
|||
class TimeLine(QtWidgets.QGraphicsItem): |
|||
"""Displays the real time.""" |
|||
|
|||
class TimePoint(QtWidgets.QGraphicsSimpleTextItem): |
|||
|
|||
instances = [] |
|||
|
|||
def __init__(self, parent, positionInSeconds): |
|||
self.__class__.instances.append(self) |
|||
m, s = divmod(positionInSeconds, 60) |
|||
text = "{}:{} min".format(str(int(m)).zfill(2), str(int(s)).zfill(2)) |
|||
super().__init__(text) |
|||
|
|||
marker = QtWidgets.QGraphicsLineItem(0, 0, 0, -10) #vertical marker to connect to the conductor line |
|||
marker.setParentItem(self) |
|||
|
|||
|
|||
def __init__(self, parent): |
|||
super().__init__() |
|||
self.parent = parent |
|||
self.gridInSeconds = 10 |
|||
api.callbacks.updateTempoTrackBlocks.append(self.redraw) |
|||
#no redraw on init. self.parent.staticPoints is not set yet. |
|||
|
|||
def paint(self, *args): |
|||
pass |
|||
def boundingRect(self, *args): |
|||
return oneRectToReturnThemAll |
|||
|
|||
def redraw(self, staticRepresentationList): |
|||
if not self.parent.staticPoints: |
|||
return None |
|||
removeInstancesFromScene(self.TimePoint) |
|||
sliceStartInSeconds = 0 #counted, not calculated |
|||
gridCounter = 0 # int("how often was a gridMarker generated") |
|||
result = [] |
|||
for nowPoint, nextPoint in pairwise(self.parent.staticPoints): |
|||
"""Values we can't calculate: |
|||
sliceEndInTicks / ticksPerSecond . sliceEndInTicks is an absolute position. |
|||
but ticksPerSecond changes with every slice. Therefore you would discard all |
|||
ticksPerSecond value except the last and calculate a wrong value. |
|||
Instead we need to calculate the seconds always in the slice and then count |
|||
the overall length. |
|||
""" |
|||
|
|||
tempoForThisSlice = abs(nowPoint["value"]) |
|||
|
|||
sliceDurationInTicks = nextPoint["position"] - nowPoint["position"] |
|||
ticksPerSecondForThisSlice = tempoForThisSlice * api.D4 / 60 # ["value"] is normalized beatsPerMinute(!) for quarter notes. calculated from "units" and "referenceTicks" |
|||
sliceDurationInSeconds = sliceDurationInTicks / ticksPerSecondForThisSlice |
|||
sliceEndInSeconds = sliceStartInSeconds + sliceDurationInSeconds |
|||
|
|||
while sliceStartInSeconds < (gridCounter+1) * self.gridInSeconds <= sliceEndInSeconds: #is the next grid marker(+1) in the current slice? |
|||
gridCounter += 1 |
|||
secondsSinceLastTempoChange = gridCounter * self.gridInSeconds - sliceStartInSeconds |
|||
|
|||
posInTicks = nowPoint["position"] + secondsSinceLastTempoChange * ticksPerSecondForThisSlice |
|||
assert nowPoint["position"] <= posInTicks <= nextPoint["position"] |
|||
result.append((posInTicks, gridCounter * self.gridInSeconds)) |
|||
|
|||
sliceStartInSeconds = sliceEndInSeconds |
|||
|
|||
#After the loop both sliceStartInSeconds and sliceEndInSeconds are equal to the overall length of the track in seconds |
|||
#Add an end marker to the results and create the qGraphicItems to display the time markers. |
|||
if nextPoint["position"] > 0: |
|||
result.append((nextPoint["position"], sliceEndInSeconds)) |
|||
|
|||
for tickPos, secPos in result: |
|||
timePoint = self.TimePoint(self, secPos) |
|||
timePoint.setParentItem(self) |
|||
timePoint.setPos(tickPos / constantsAndConfigs.ticksToPixelRatio, 10) |
|||
|
|||
|
|||
def stretchXCoordinates(self, factor): |
|||
"""Does NOT just reposition the existing items but displays a different |
|||
time grid""" |
|||
for timePoint in self.TimePoint.instances: |
|||
timePoint.setX(timePoint.pos().x() * factor) |
|||
|
|||
|
|||
|
|||
|
@ -0,0 +1,177 @@ |
|||
#! /usr/bin/env python3 |
|||
# -*- coding: utf-8 -*- |
|||
""" |
|||
Copyright 2017, Nils Hilbricht, Germany ( https://www.hilbricht.net ) |
|||
|
|||
This file is part of Laborejo ( https://www.laborejo.org ) |
|||
|
|||
Laborejo is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
""" |
|||
|
|||
import logging; logging.info("import {}".format(__file__)) |
|||
|
|||
import engine.api as api |
|||
from PyQt5 import QtGui |
|||
|
|||
class ConstantsAndConfigs(object): |
|||
def __init__(self): |
|||
self.ticksToPixelRatio = 128*3*5 #bigger value means less space between notes #api.D128 and 128*3*5 is also good. This gets changed during runtime as "Stretch Factor". Also: the QSettings save and reload this on a per-file basis. |
|||
#a quarter note has 53760 ticks. that is 53760 / 1920 = 28 pixels. |
|||
self.stafflineGap = 7 #it is 7 because it is an odd number, which has a middle. 123 4 567 which translates 1:1 to pixels. |
|||
self.beamHeight = 2 |
|||
self.magicPixel = 4 |
|||
self.negativeMagicPixel = -2 |
|||
self.trackHeight = self.stafflineGap * 10 |
|||
self.trackHeight = self.stafflineGap * 12 |
|||
self.gridRhythm = api.D4 #Default value is quarter. The QSettings save and reload this on a per-file basis. |
|||
self.gridOpacity = 0.1 #this is only the default value. Once changed a QSetting will be used instead. |
|||
self.noteHeadMode = True #False means rectangle noteheads |
|||
self.followPlayhead = True #camera follows the playhead during playback. |
|||
self.duringPlayback = False #set by a callback. |
|||
self.velocityToPixelRatio = 2 #2.0 is the default. 128 pixels maximum. |
|||
|
|||
self.availableEditModes = ("notation", "cc", "block") |
|||
|
|||
self.ccViewValue = 0 #0-127. Only has effect if ccView is True |
|||
self.ccViewCallbacks = [] #list of functions. For example the main Window CC spin box and the block Editor CC spin box. |
|||
|
|||
self._snapToGrid = True # For CC point and Tempo point placements |
|||
self.snapToGridCallbacks = [] # see ccViewCallbacks |
|||
|
|||
self.zoomFactor = 1 # #Initial Zoom Level. Also hardcoded into scoreView.zoomNull |
|||
self.maximumZoomOut = 0.25 |
|||
|
|||
|
|||
#fonts which are used in the QGraphicsScenes. They are special because they don't scale with the DPI. |
|||
self.fontDB = QtGui.QFontDatabase() |
|||
#fid = self.fontDB.addApplicationFont("gui/resources/freesans.ttf") |
|||
#if fid == -1: |
|||
# raise ValueError("Freesans.ttf not loaded ") |
|||
|
|||
fid = self.fontDB.addApplicationFont(":euterpe.ttf") |
|||
if fid == -1: |
|||
raise ValueError("euterpe.ttf not loaded. Make sure resources were generated") |
|||
|
|||
#For easier programWide access move these to constantsAndConfigs |
|||
#constantsAndConfigs.theFont = self.fontDB.font("FreeSans", "", 13) |
|||
#constantsAndConfigs.theFont.setPixelSize(13) #It is very important to set the pixel size before setting the font to the text |
|||
self.musicFont = self.fontDB.font("Euterpe", "", 13) |
|||
self.musicFont.setPixelSize(13) #It is very important to set the pixel size before setting the font to the text |
|||
|
|||
self.dynamics = ["f", "ff", "p", "pp", "mf", "mp", "tacet"] |
|||
|
|||
self.prettyRhythms = [ #list because we need item order |
|||
(api.DL, "0.25 Longa"), |
|||
(api.DB, "0.5 Brevis"), |
|||
(api.D1, "1 Whole"), |
|||
(api.D2, "2 Half"), |
|||
(api.D4, "4 Quarter"), |
|||
(api.D8, "8 Eigth"), |
|||
(api.D16, "16 Sixteenth"), |
|||
(api.D32, "32 Thirthy-Second"), |
|||
(api.D64, "64 Sixty-Fourth"), |
|||
(api.D128, "128 Hundred Twenty-Eighth "), |
|||
(api.D256, "256 Two-hundred Fifty-Sixth"), |
|||
] |
|||
self.prettyRhythmsStrings = [v for k,v in self.prettyRhythms] |
|||
self.prettyRhythmsValues = [k for k,v in self.prettyRhythms] |
|||
|
|||
self.prettyExtendedRhythms = [ #list because we need item order |
|||
(api.DL, "0.25 Longa"), |
|||
(api.DB, "0.5 Brevis"), |
|||
(api.D1, "1 Whole"), |
|||
(api.D2, "2 Half "), |
|||
(api.D4, "4 Quarter"), |
|||
(api.D8, "8 Eigth"), |
|||
(api.D16, "16 Sixteenth"), |
|||
(api.D32, "32 Thirthy-Second"), |
|||
(api.D64, "64 Sixty-Fourth"), |
|||
(api.D128, "128 Hundred Twenty-Eighth"), |
|||
|
|||
(1.5*api.DL, "Dotted 0.25 Longa"), |
|||
(1.5*api.DB, "Dotted 0.5 Brevis"), |
|||
(1.5*api.D1, "Dotted 1 Whole"), |
|||
(1.5*api.D2, "Dotted 2 Half"), |
|||
(1.5*api.D4, "Dotted 4 Quarter"), |
|||
(1.5*api.D8, "Dotted 8 Eigth "), |
|||
(1.5*api.D16, "Dotted 16 Sixteenth"), |
|||
(1.5*api.D32, "Dotted 32 Thirthy-Second"), |
|||
(1.5*api.D64, "Dotted 64 Sixty-Fourth "), |
|||
(1.5*api.D128, "Dotted 128 Hundred Twenty-Eighth "), |
|||
|
|||
] |
|||
self.prettyExtendedRhythmsStrings = [v for k,v in self.prettyExtendedRhythms] |
|||
self.prettyExtendedRhythmsValues = [k for k,v in self.prettyExtendedRhythms] |
|||
|
|||
#use with constantsAndConfigs.musicFont |
|||
self.realNoteDisplay = { |
|||
api.DB : "𝅜", |
|||
api.D1 : "𝅝", |
|||
api.D2 : "𝅗𝅥", |
|||
api.D4 : "𝅘𝅥", |
|||
api.D8 : "𝅘𝅥𝅮", |
|||
api.D16 : "𝅘𝅥𝅯", |
|||
api.D32 : "𝅘𝅥𝅰", |
|||
api.D64 : "𝅘𝅥𝅱", |
|||
api.D128 : "𝅘𝅥𝅲", |
|||
|
|||
1.5 * api.DB : "𝅜𝅭", #dotted DB |
|||
1.5 * api.D1 : "𝅝𝅭", #dotted D1 |
|||
1.5 * api.D2 : "𝅗𝅥𝅭", #dotted D2 |
|||
1.5 * api.D4 : "𝅘𝅥𝅭", #dotted D4 |
|||
1.5 * api.D8 : "𝅘𝅥𝅮𝅭", #dotted D8 |
|||
1.5 * api.D16 : "𝅘𝅥𝅯𝅭", #dotted D16 |
|||
1.5 * api.D32 : "𝅘𝅥𝅰𝅭", #dotted D32 |
|||
1.5 * api.D64 : "𝅘𝅥𝅱𝅭", #dotted D64 |
|||
1.5 * api.D128 : "𝅘𝅥𝅲𝅭", #dotted D128 |
|||
|
|||
2.25 * api.DB : "𝅜𝅭", #double dotted DB |
|||
2.25 * api.D1 : "𝅝𝅭", #double dotted D1 |
|||
2.25 * api.D2 : "𝅗𝅥𝅭", #double dotted D2 |
|||
2.25 * api.D4 : "𝅘𝅥𝅭", #double dotted D4 |
|||
2.25 * api.D8 : "𝅘𝅥𝅮𝅭", #double dotted D8 |
|||
2.25 * api.D16 : "𝅘𝅥𝅯𝅭", #double dotted D16 |
|||
2.25 * api.D32 : "𝅘𝅥𝅰𝅭", #double dotted D32 |
|||
2.25 * api.D64 : "𝅘𝅥𝅱𝅭", #double dotted D64 |
|||
2.25 * api.D128 : "𝅘𝅥𝅲𝅭", #double dotted D128 |
|||
} |
|||
|
|||
|
|||
self.commonNotes = [ #list because we need item order |
|||
(api.D1, "𝅝"), #D1 |
|||
(api.D2 , "𝅗𝅥"), #D2 |
|||
(api.D4 , "𝅘𝅥"), #D4 |
|||
(api.D8 , "𝅘𝅥𝅮"), #D8 |
|||
|
|||
(1.5 * api.D1 , "𝅝𝅭"), #dotted D1 |
|||
(1.5 * api.D2 , "𝅗𝅥𝅭"), #dotted D2 |
|||
(1.5 * api.D4 , "𝅘𝅥𝅭"), #dotted D4 |
|||
(1.5 * api.D8 , "𝅘𝅥𝅮𝅭"), #dotted D8 |
|||
] |
|||
|
|||
self.realNotesStrings = [v for k,v in self.commonNotes] |
|||
self.realNotesValues = [k for k,v in self.commonNotes] |
|||
|
|||
@property |
|||
def snapToGrid(self): |
|||
return self._snapToGrid |
|||
|
|||
@snapToGrid.setter |
|||
def snapToGrid(self, value): |
|||
self._snapToGrid = value |
|||
for func in self.snapToGridCallbacks: |
|||
func() |
|||
|
|||
|
|||
constantsAndConfigs = ConstantsAndConfigs() #singleton |
@ -0,0 +1,242 @@ |
|||
#! /usr/bin/env python3 |
|||
# -*- coding: utf-8 -*- |
|||
""" |
|||
Copyright 2017, Nils Hilbricht, Germany ( https://www.hilbricht.net ) |
|||
|
|||
This file is part of Laborejo ( https://www.laborejo.org ) |
|||
|
|||
Laborejo is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
""" |
|||
|
|||
from PyQt5 import QtCore, QtGui, QtWidgets, QtWidgets |
|||
from .constantsAndConfigs import constantsAndConfigs |
|||
import engine.api as api |
|||
|
|||
pen = QtGui.QPen() |
|||
pen.setCapStyle(QtCore.Qt.RoundCap) |
|||
pen.setJoinStyle(QtCore.Qt.RoundJoin) |
|||
pen.setWidth(2) |
|||
#pen.setColor(QtGui.QColor("red")) |
|||
|
|||
class PitchCursor(QtWidgets.QGraphicsRectItem): |
|||
def __init__(self): |
|||
"""Does not need the actual dotOnLine. |
|||
this is done by the parent cursor. |
|||
This is just a fancy rect.""" |
|||
#super().__init__(constantsAndConfigs.magicPixel, 2, constantsAndConfigs.magicPixel*3, constantsAndConfigs.stafflineGap/3) |
|||
super().__init__(0, 1, constantsAndConfigs.magicPixel*3, 5) |
|||
self.setBrush(QtGui.QColor("cyan")) |
|||
self.setPen(pen) |
|||
self.setEnabled(False) |
|||
|
|||
class PositionCursor(QtWidgets.QGraphicsRectItem): |
|||
def __init__(self): |
|||
"""Does not need the actual position. |
|||
this is done by the parent cursor |
|||
This is just a fancy rect.""" |
|||
super().__init__(-1.5*constantsAndConfigs.magicPixel, -3.5*constantsAndConfigs.stafflineGap, constantsAndConfigs.magicPixel*1.5, 7*constantsAndConfigs.stafflineGap) |
|||
self.setBrush(QtGui.QColor("cyan")) |
|||
self.setPen(pen) |
|||
self.setScale(0.8) |
|||
self.setEnabled(False) |
|||
|
|||
class Cursor(QtWidgets.QGraphicsItemGroup): |
|||
"""A cursor that shows the vertical |
|||
as well as the horizontal position |
|||
""" |
|||
def __init__(self): |
|||
super().__init__() |
|||
self.cursorExportObject = None #the last cached cursor object aka the current cursor position |
|||
self.pitch = PitchCursor() |
|||
self.addToGroup(self.pitch) |
|||
self.position = PositionCursor() |
|||
self.addToGroup(self.position) |
|||
api.callbacks.setCursor.append(self.setCursor) |
|||
self.setEnabled(False) |
|||
|
|||
def clearItemHighlight(self): |
|||
"""Gets called before a track changes. Most of the time when a new item is inserted/deleted. |
|||
This means the gui track will be recreated and a current highlight on an item might get |
|||
deleted while still on the item. This results in a qt crash |
|||
""" |
|||
Cursor.hightlightEffect = QtWidgets.QGraphicsColorizeEffect() #default strength of the effect is 1.0 |
|||
Cursor.hightlightEffect.setColor(QtGui.QColor("cyan")) |
|||
Cursor.hightlightEffect.setStrength(0.7) #opacity of the effect |
|||
|
|||
#Cursor.stafflineEffect = QtWidgets.QGraphicsColorizeEffect() #default strength of the effect is 1.0 |
|||
#Cursor.stafflineEffect.setColor(QtGui.QColor("cyan")) |
|||
#Cursor.stafflineEffect.setStrength(1) #opacity of the effect |
|||
|
|||
Cursor.stafflineEffect = QtWidgets.QGraphicsDropShadowEffect() |
|||
Cursor.stafflineEffect.setColor(QtGui.QColor("black")) |
|||
Cursor.stafflineEffect.setOffset(0,0) |
|||
Cursor.stafflineEffect.setBlurRadius(5) |
|||
|
|||
def setCursor(self, cursorExportObject): |
|||
self.cursorExportObject = cursorExportObject |
|||
self.scene().parentView.setFocus() |
|||
|
|||
self.pitch.setY(constantsAndConfigs.stafflineGap * cursorExportObject["dotOnLine"] / 2 - 3) #the same as a notehead |
|||
|
|||
x = cursorExportObject["tickindex"] / constantsAndConfigs.ticksToPixelRatio |
|||
#Finally shift the Position to the correct track. Trackindex from 0, naturally. |
|||
#self.setPos(x, constantsAndConfigs.timeLineOffsetNoteEditor + cursorExportObject["trackIndex"] * constantsAndConfigs.trackHeight) |
|||
currentGuiTrack = self.scene().tracks[cursorExportObject["trackId"]] |
|||
self.setPos(x, currentGuiTrack.y() ) |
|||
|
|||
try: |
|||
guiItemAtCursor = currentGuiTrack.itemById(cursorExportObject["itemId"]) |
|||
if not guiItemAtCursor.staticItem["completeDuration"] > 0: |
|||
#guiItemAtCursor has no duration. is type guiItemAtCursor.staticItem["type"] |
|||
Cursor.hightlightEffect.setEnabled(True) |
|||
guiItemAtCursor.setGraphicsEffect(Cursor.hightlightEffect) |
|||
else: |
|||
Cursor.hightlightEffect.setEnabled(False) |
|||
|
|||
except StopIteration: |
|||
Cursor.hightlightEffect.setEnabled(False) #happens only on startup or appending (which is the same) |
|||
|
|||
#Highlight the current staffline |
|||
if currentGuiTrack.staticExportItem["double"] and cursorExportObject["dotOnLine"] in (8, 10, 12, 14, 16): |
|||
lineNumber = int(cursorExportObject["dotOnLine"] / 2) + 1 #gui-stafflines are counted from 0 to n, top to bottom (listindex) while backend stafflines begin on -4 |
|||
currentGuiTrack.staffLines[lineNumber].setGraphicsEffect(Cursor.stafflineEffect) |
|||
Cursor.stafflineEffect.setEnabled(True) |
|||
elif cursorExportObject["dotOnLine"] in (-4, -2, 0, 2, 4): |
|||
lineNumber = int(cursorExportObject["dotOnLine"] / 2) + 2 #gui-stafflines are counted from 0 to n, top to bottom (listindex) while backend stafflines begin on -4 |
|||
currentGuiTrack.staffLines[lineNumber].setGraphicsEffect(Cursor.stafflineEffect) |
|||
Cursor.stafflineEffect.setEnabled(True) |
|||
else: |
|||
Cursor.stafflineEffect.setEnabled(False) |
|||
|
|||
class Playhead(QtWidgets.QGraphicsLineItem): |
|||
def __init__(self, parentScoreScene): |
|||
super().__init__(0, 0, 0, 0) # (x1, y1, x2, y2) |
|||
self.parentScoreScene = parentScoreScene |
|||
p = QtGui.QPen() |
|||
p.setColor(QtGui.QColor("red")) |
|||
p.setCosmetic(True) |
|||
self.setPen(p) |
|||
self.setAcceptHoverEvents(True) |
|||
api.callbacks.setPlaybackTicks.append(self.setCursorPosition) |
|||
api.callbacks.tracksChanged.append(self.setLineToWindowHeigth) #for new tracks |
|||
api.callbacks.updateTempoTrack.append(self.setLineToWindowHeigth) |
|||
self.setFlags(QtWidgets.QGraphicsItem.ItemIsMovable) |
|||
self.setAcceptedMouseButtons(QtCore.Qt.LeftButton) |
|||
self.setZValue(90) |
|||
#self.parentScoreScene.parentView.verticalScrollBar().valueChanged.connect(self.setLineToWindowHeigth) |
|||
#self.hide() |
|||
#self.maxHeight = QtWidgets.QDesktopWidget().geometry().height() #we really hope the screen resolution does not change during the session. |
|||
|
|||
def setCursorPosition(self, tickindex:int, playbackStatus:bool): |
|||
"""Set the playhead to the right position, but keep the viewport stable. |
|||
Shift the entire "page" if the cursor becomes invisible because its steps outside the viewport""" |
|||
self.setLineToWindowHeigth() |
|||
x = tickindex / constantsAndConfigs.ticksToPixelRatio |
|||
self.setX(x) |
|||
if constantsAndConfigs.followPlayhead and playbackStatus: |
|||
scenePos = self.parentScoreScene.parentView.mapFromScene(self.pos()) |
|||
cursorViewPosX = scenePos.x() #the cursor position in View coordinates |
|||
width = self.parentScoreScene.parentView.geometry().width() |
|||
if cursorViewPosX <= 0 or cursorViewPosX >= width: #"pageflip" |
|||
self.parentScoreScene.parentView.horizontalScrollBar().setValue(x * constantsAndConfigs.zoomFactor) |
|||
|
|||
|
|||
def _Deprecated__ScrollingVariant___setCursorPosition(self, tickindex): |
|||
"""the tickindex to pixel index on the x axis is a fixed 1:n |
|||
relation. What you see is where you are. No jumps, the playhead |
|||
stays on course.""" |
|||
self.setLineToWindowHeigth() |
|||
x = tickindex / constantsAndConfigs.ticksToPixelRatio |
|||
self.setX(x) |
|||
if constantsAndConfigs.followPlayhead and api.playbackStatus(): |
|||
#self.parentScoreScene.parentView.centerOn(self) Do not use center on. It centers for Y as well which creates a recursion and the score gets taller and taller. |
|||
#self.scene().parentView.horizontalScrollBar().setValue(x - 150) |
|||
#xV = self.parentScoreScene.parentView.mapFromScene(x,0).x() |
|||
self.parentScoreScene.parentView.horizontalScrollBar().setValue(x - 150) #x does not with zoom levels. the bar drifts away. |
|||
|
|||
def setLineToWindowHeigth(self, *args): |
|||
h = self.parentScoreScene.cachedSceneHeight |
|||
self.setLine(0, 0, 0, h) #(x1, y1, x2, y2) |
|||
|
|||
def mouseMoveEvent(self, event): |
|||
"""Only allow movement in Y direction. |
|||
Only triggered when dragging.""" |
|||
#super().mouseMoveEvent(event) allows free movement through Qt. Don't call that. |
|||
p = event.scenePos().x() |
|||
if p < 0: |
|||
p = 0 |
|||
#self.setPos(p, self.scene().parentView.mapToScene(0, 0).y()) |
|||
self.setX(p) |
|||
api.seek(p * constantsAndConfigs.ticksToPixelRatio) |
|||
event.accept() |
|||
|
|||
def mouseReleaseEvent(self, event): |
|||
if constantsAndConfigs.snapToGrid: |
|||
x = event.scenePos().x() * constantsAndConfigs.ticksToPixelRatio |
|||
p = round(x / constantsAndConfigs.gridRhythm) * constantsAndConfigs.gridRhythm |
|||
if p < 0: |
|||
p = 0 |
|||
api.seek(p) |
|||
|
|||
def hoverEnterEvent(self, event): |
|||
self.setCursor(QtCore.Qt.SizeHorCursor) |
|||
self.update() #the default implementation calls this. event.accept/ignore has no effect. |
|||
|
|||
def hoverLeaveEvent(self, event): |
|||
self.unsetCursor() |
|||
self.update() #the default implementation calls this. event.accept/ignore has no effect. |
|||
|
|||
class Selection(QtWidgets.QGraphicsRectItem): |
|||
"""A semi-transparent rectangle that shows the current selection""" |
|||
def __init__(self): |
|||
super().__init__(0, 0, 0, 0) |
|||
|
|||
self.validBrush = QtGui.QBrush(QtGui.QColor("grey")) |
|||
self.invalidBrush = QtGui.QBrush(QtGui.QColor("grey")) |
|||
self.invalidBrush.setStyle(QtCore.Qt.DiagCrossPattern) |
|||
|
|||
self.setPen(pen) |
|||
self.setOpacity(0.2) |
|||
self.setEnabled(False) |
|||
api.callbacks.setSelection.append(self.setSelection) |
|||
|
|||
def setSelection(self, tupleOfCursorExportObjects): |
|||
if tupleOfCursorExportObjects: |
|||
validSelection, topleftCursorObject, bottomRightCursorObject = tupleOfCursorExportObjects |
|||
if validSelection: |
|||
self.setBrush(self.validBrush) |
|||
else: |
|||
self.setBrush(self.invalidBrush) |
|||
|
|||
topGuiTrack = self.scene().tracks[topleftCursorObject["trackId"]] |
|||
bottomGuiTrack = self.scene().tracks[bottomRightCursorObject["trackId"]] |
|||
|
|||
#y = constantsAndConfigs.timeLineOffsetNoteEditor + topleftCursorObject["trackIndex"] * constantsAndConfigs.trackHeight - constantsAndConfigs.trackHeight/2 |
|||
#h = (bottomRightCursorObject["trackIndex"] - topleftCursorObject["trackIndex"]) * constantsAndConfigs.trackHeight + constantsAndConfigs.trackHeight |
|||
|
|||
x = topleftCursorObject["tickindex"] / constantsAndConfigs.ticksToPixelRatio |
|||
w = (bottomRightCursorObject["tickindex"] - topleftCursorObject["tickindex"]) / constantsAndConfigs.ticksToPixelRatio |
|||
y = topGuiTrack.y() - constantsAndConfigs.trackHeight/2 |
|||
|
|||
if bottomGuiTrack.staticExportItem["double"]: |
|||
h = bottomGuiTrack.y() - y + constantsAndConfigs.trackHeight |
|||
else: |
|||
h = bottomGuiTrack.y() - y + constantsAndConfigs.trackHeight/2 |
|||
|
|||
|
|||
self.setRect(0, 0, w-3, h) #substract a few pixels to make it look less ambigious if the last item on the right edge is included or not (it is not) |
|||
self.setPos(x,y) |
|||
self.setVisible(True) |
|||
else: #no selection |
|||
self.setVisible(False) |
@ -0,0 +1,49 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<ui version="4.0"> |
|||
<class>Form</class> |
|||
<widget class="QWidget" name="Form"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>0</x> |
|||
<y>0</y> |
|||
<width>585</width> |
|||
<height>130</height> |
|||
</rect> |
|||
</property> |
|||
<property name="windowTitle"> |
|||
<string>Form</string> |
|||
</property> |
|||
<layout class="QGridLayout" name="gridLayout"> |
|||
<item row="0" column="1"> |
|||
<widget class="QPushButton" name="bass"> |
|||
<property name="text"> |
|||
<string>[2] Bass</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="1" column="0"> |
|||
<widget class="QPushButton" name="pushButton"> |
|||
<property name="text"> |
|||
<string>[3] Treble +Oct</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="1" column="1"> |
|||
<widget class="QPushButton" name="pushButton_2"> |
|||
<property name="text"> |
|||
<string>[4] Bass -Oct</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="0" column="0"> |
|||
<widget class="QPushButton" name="treble"> |
|||
<property name="text"> |
|||
<string>[1] Treble</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
<resources/> |
|||
<connections/> |
|||
</ui> |
@ -0,0 +1,956 @@ |
|||
# -*- coding: utf-8 -*- |
|||
|
|||
# Form implementation generated from reading ui file 'mainwindow.ui' |
|||
# |
|||
# Created by: PyQt5 UI code generator 5.11.3 |
|||
# |
|||
# WARNING! All changes made in this file will be lost! |
|||
|
|||
from PyQt5 import QtCore, QtGui, QtWidgets |
|||
|
|||
class Ui_MainWindow(object): |
|||
def setupUi(self, MainWindow): |
|||
MainWindow.setObjectName("MainWindow") |
|||
MainWindow.resize(1036, 625) |
|||
self.centralwidget = QtWidgets.QWidget(MainWindow) |
|||
self.centralwidget.setObjectName("centralwidget") |
|||
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget) |
|||
self.verticalLayout_2.setObjectName("verticalLayout_2") |
|||
self.mainStackWidget = QtWidgets.QStackedWidget(self.centralwidget) |
|||
self.mainStackWidget.setObjectName("mainStackWidget") |
|||
self.verticalLayout_2.addWidget(self.mainStackWidget) |
|||
MainWindow.setCentralWidget(self.centralwidget) |
|||
self.statusbar = QtWidgets.QStatusBar(MainWindow) |
|||
self.statusbar.setObjectName("statusbar") |
|||
MainWindow.setStatusBar(self.statusbar) |
|||
self.toolBar = QtWidgets.QToolBar(MainWindow) |
|||
self.toolBar.setMovable(False) |
|||
self.toolBar.setFloatable(False) |
|||
self.toolBar.setObjectName("toolBar") |
|||
MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.toolBar) |
|||
self.menubar = QtWidgets.QMenuBar(MainWindow) |
|||
self.menubar.setGeometry(QtCore.QRect(0, 0, 1036, 20)) |
|||
self.menubar.setObjectName("menubar") |
|||
self.menuHelp = QtWidgets.QMenu(self.menubar) |
|||
self.menuHelp.setObjectName("menuHelp") |
|||
self.menuObjects = QtWidgets.QMenu(self.menubar) |
|||
self.menuObjects.setObjectName("menuObjects") |
|||
self.menuGeneric = QtWidgets.QMenu(self.menubar) |
|||
self.menuGeneric.setEnabled(True) |
|||
self.menuGeneric.setObjectName("menuGeneric") |
|||
self.menuType_Here = QtWidgets.QMenu(self.menuGeneric) |
|||
self.menuType_Here.setObjectName("menuType_Here") |
|||
self.menuTracks = QtWidgets.QMenu(self.menubar) |
|||
self.menuTracks.setObjectName("menuTracks") |
|||
self.menuEdit = QtWidgets.QMenu(self.menubar) |
|||
self.menuEdit.setObjectName("menuEdit") |
|||
self.menuView = QtWidgets.QMenu(self.menubar) |
|||
self.menuView.setObjectName("menuView") |
|||
self.menuMode = QtWidgets.QMenu(self.menuView) |
|||
self.menuMode.setObjectName("menuMode") |
|||
self.menu = QtWidgets.QMenu(self.menubar) |
|||
self.menu.setEnabled(False) |
|||
self.menu.setObjectName("menu") |
|||
self.menu_2 = QtWidgets.QMenu(self.menubar) |
|||
self.menu_2.setEnabled(False) |
|||
self.menu_2.setObjectName("menu_2") |
|||
self.menuEdit_MusicItem = QtWidgets.QMenu(self.menubar) |
|||
self.menuEdit_MusicItem.setObjectName("menuEdit_MusicItem") |
|||
self.menuEdit_2 = QtWidgets.QMenu(self.menubar) |
|||
self.menuEdit_2.setObjectName("menuEdit_2") |
|||
self.menuToolbox = QtWidgets.QMenu(self.menubar) |
|||
self.menuToolbox.setObjectName("menuToolbox") |
|||
self.menuOrder = QtWidgets.QMenu(self.menuToolbox) |
|||
self.menuOrder.setObjectName("menuOrder") |
|||
self.menuInsert = QtWidgets.QMenu(self.menuToolbox) |
|||
self.menuInsert.setObjectName("menuInsert") |
|||
self.menuMIDI = QtWidgets.QMenu(self.menuToolbox) |
|||
self.menuMIDI.setObjectName("menuMIDI") |
|||
self.menuLilypond = QtWidgets.QMenu(self.menubar) |
|||
self.menuLilypond.setObjectName("menuLilypond") |
|||
MainWindow.setMenuBar(self.menubar) |
|||
self.leftToolBar = QtWidgets.QToolBar(MainWindow) |
|||
self.leftToolBar.setEnabled(True) |
|||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred) |
|||
sizePolicy.setHorizontalStretch(0) |
|||
sizePolicy.setVerticalStretch(0) |
|||
sizePolicy.setHeightForWidth(self.leftToolBar.sizePolicy().hasHeightForWidth()) |
|||
self.leftToolBar.setSizePolicy(sizePolicy) |
|||
self.leftToolBar.setMinimumSize(QtCore.QSize(26, 0)) |
|||
self.leftToolBar.setMaximumSize(QtCore.QSize(26, 16777215)) |
|||
self.leftToolBar.setMovable(False) |
|||
self.leftToolBar.setAllowedAreas(QtCore.Qt.LeftToolBarArea) |
|||
self.leftToolBar.setFloatable(False) |
|||
self.leftToolBar.setObjectName("leftToolBar") |
|||
MainWindow.addToolBar(QtCore.Qt.LeftToolBarArea, self.leftToolBar) |
|||
self.actionUp = QtWidgets.QAction(MainWindow) |
|||
self.actionUp.setObjectName("actionUp") |
|||
self.actionDown = QtWidgets.QAction(MainWindow) |
|||
self.actionDown.setObjectName("actionDown") |
|||
self.actionLeft = QtWidgets.QAction(MainWindow) |
|||
self.actionLeft.setObjectName("actionLeft") |
|||
self.actionRight = QtWidgets.QAction(MainWindow) |
|||
self.actionRight.setObjectName("actionRight") |
|||
self.actionModal1 = QtWidgets.QAction(MainWindow) |
|||
self.actionModal1.setObjectName("actionModal1") |
|||
self.actionModal2 = QtWidgets.QAction(MainWindow) |
|||
self.actionModal2.setObjectName("actionModal2") |
|||
self.actionModal3 = QtWidgets.QAction(MainWindow) |
|||
self.actionModal3.setObjectName("actionModal3") |
|||
self.actionModal4 = QtWidgets.QAction(MainWindow) |
|||
self.actionModal4.setObjectName("actionModal4") |
|||
self.actionModal5 = QtWidgets.QAction(MainWindow) |
|||
self.actionModal5.setObjectName("actionModal5") |
|||
self.actionModal6 = QtWidgets.QAction(MainWindow) |
|||
self.actionModal6.setObjectName("actionModal6") |
|||
self.actionModal7 = QtWidgets.QAction(MainWindow) |
|||
self.actionModal7.setObjectName("actionModal7") |
|||
self.actionModal8 = QtWidgets.QAction(MainWindow) |
|||
self.actionModal8.setObjectName("actionModal8") |
|||
self.actionModal9 = QtWidgets.QAction(MainWindow) |
|||
self.actionModal9.setObjectName("actionModal9") |
|||
self.actionModal0 = QtWidgets.QAction(MainWindow) |
|||
self.actionModal0.setObjectName("actionModal0") |
|||
self.actionDelete = QtWidgets.QAction(MainWindow) |
|||
self.actionDelete.setObjectName("actionDelete") |
|||
self.actionBackspace = QtWidgets.QAction(MainWindow) |
|||
self.actionBackspace.setObjectName("actionBackspace") |
|||
self.actionShift_modal1 = QtWidgets.QAction(MainWindow) |
|||
self.actionShift_modal1.setObjectName("actionShift_modal1") |
|||
self.actionShift_modal2 = QtWidgets.QAction(MainWindow) |
|||
self.actionShift_modal2.setObjectName("actionShift_modal2") |
|||
self.actionShift_modal3 = QtWidgets.QAction(MainWindow) |
|||
self.actionShift_modal3.setObjectName("actionShift_modal3") |
|||
self.actionShift_modal4 = QtWidgets.QAction(MainWindow) |
|||
self.actionShift_modal4.setObjectName("actionShift_modal4") |
|||
self.actionShift_modal5 = QtWidgets.QAction(MainWindow) |
|||
self.actionShift_modal5.setObjectName("actionShift_modal5") |
|||
self.actionShift_modal6 = QtWidgets.QAction(MainWindow) |
|||
self.actionShift_modal6.setObjectName("actionShift_modal6") |
|||
self.actionShift_modal7 = QtWidgets.QAction(MainWindow) |
|||
self.actionShift_modal7.setObjectName("actionShift_modal7") |
|||
self.actionShift_modal8 = QtWidgets.QAction(MainWindow) |
|||
self.actionShift_modal8.setObjectName("actionShift_modal8") |
|||
self.actionShift_modal9 = QtWidgets.QAction(MainWindow) |
|||
self.actionShift_modal9.setObjectName("actionShift_modal9") |
|||
self.actionShift_modal0 = QtWidgets.QAction(MainWindow) |
|||
self.actionShift_modal0.setObjectName("actionShift_modal0") |
|||
self.actionAddCursorNoteToChord = QtWidgets.QAction(MainWindow) |
|||
self.actionAddCursorNoteToChord.setShortcutContext(QtCore.Qt.WindowShortcut) |
|||
self.actionAddCursorNoteToChord.setObjectName("actionAddCursorNoteToChord") |
|||
self.actionDeleteCursorNoteFromChord = QtWidgets.QAction(MainWindow) |
|||
self.actionDeleteCursorNoteFromChord.setObjectName("actionDeleteCursorNoteFromChord") |
|||
self.actionAdd_Track = QtWidgets.QAction(MainWindow) |
|||
self.actionAdd_Track.setObjectName("actionAdd_Track") |
|||
self.actionTrackUp = QtWidgets.QAction(MainWindow) |
|||
self.actionTrackUp.setObjectName("actionTrackUp") |
|||
self.actionTrackDown = QtWidgets.QAction(MainWindow) |
|||
self.actionTrackDown.setObjectName("actionTrackDown") |
|||
self.actionHead = QtWidgets.QAction(MainWindow) |
|||
self.actionHead.setObjectName("actionHead") |
|||
self.actionTail = QtWidgets.QAction(MainWindow) |
|||
self.actionTail.setObjectName("actionTail") |
|||
self.actionTrackFirst = QtWidgets.QAction(MainWindow) |
|||
self.actionTrackFirst.setObjectName("actionTrackFirst") |
|||
self.actionTrackLast = QtWidgets.QAction(MainWindow) |
|||
self.actionTrackLast.setObjectName("actionTrackLast") |
|||
self.actionSharpen_Note = QtWidgets.QAction(MainWindow) |
|||
self.actionSharpen_Note.setObjectName("actionSharpen_Note") |
|||
self.actionFlatten_Note = QtWidgets.QAction(MainWindow) |
|||
self.actionFlatten_Note.setObjectName("actionFlatten_Note") |
|||
self.actionMeasureLeft = QtWidgets.QAction(MainWindow) |
|||
self.actionMeasureLeft.setObjectName("actionMeasureLeft") |
|||
self.actionMeasureRight = QtWidgets.QAction(MainWindow) |
|||
self.actionMeasureRight.setObjectName("actionMeasureRight") |
|||
self.actionStep_Up = QtWidgets.QAction(MainWindow) |
|||
self.actionStep_Up.setObjectName("actionStep_Up") |
|||
self.actionStep_Down = QtWidgets.QAction(MainWindow) |
|||
self.actionStep_Down.setObjectName("actionStep_Down") |
|||
self.actionOctave_Up = QtWidgets.QAction(MainWindow) |
|||
self.actionOctave_Up.setObjectName("actionOctave_Up") |
|||
self.actionOctave_Down = QtWidgets.QAction(MainWindow) |
|||
self.actionOctave_Down.setObjectName("actionOctave_Down") |
|||
self.actionAugment = QtWidgets.QAction(MainWindow) |
|||
self.actionAugment.setObjectName("actionAugment") |
|||
self.actionDiminish = QtWidgets.QAction(MainWindow) |
|||
self.actionDiminish.setObjectName("actionDiminish") |
|||
self.actionAppend_Block = QtWidgets.QAction(MainWindow) |
|||
self.actionAppend_Block.setObjectName("actionAppend_Block") |
|||
self.actionSplit_Current_Block = QtWidgets.QAction(MainWindow) |
|||
self.actionSplit_Current_Block.setObjectName("actionSplit_Current_Block") |
|||
self.actionDuplicate = QtWidgets.QAction(MainWindow) |
|||
self.actionDuplicate.setObjectName("actionDuplicate") |
|||
self.actionCreate_Linked_Copy = QtWidgets.QAction(MainWindow) |
|||
self.actionCreate_Linked_Copy.setObjectName("actionCreate_Linked_Copy") |
|||
self.actionDelete_Current_Block = QtWidgets.QAction(MainWindow) |
|||
self.actionDelete_Current_Block.setObjectName("actionDelete_Current_Block") |
|||
self.actionToggle_Notehead_Rectangles = QtWidgets.QAction(MainWindow) |
|||
self.actionToggle_Notehead_Rectangles.setCheckable(True) |
|||
self.actionToggle_Notehead_Rectangles.setObjectName("actionToggle_Notehead_Rectangles") |
|||
self.actionPlayPause = QtWidgets.QAction(MainWindow) |
|||
self.actionPlayPause.setObjectName("actionPlayPause") |
|||
self.actionPlayFromBeginning = QtWidgets.QAction(MainWindow) |
|||
self.actionPlayFromBeginning.setObjectName("actionPlayFromBeginning") |
|||
self.actionFollow_Playhead = QtWidgets.QAction(MainWindow) |
|||
self.actionFollow_Playhead.setCheckable(True) |
|||
self.actionFollow_Playhead.setChecked(True) |
|||
self.actionFollow_Playhead.setObjectName("actionFollow_Playhead") |
|||
self.actionPlayFromEditCursor = QtWidgets.QAction(MainWindow) |
|||
self.actionPlayFromEditCursor.setObjectName("actionPlayFromEditCursor") |
|||
self.actionStaccato = QtWidgets.QAction(MainWindow) |
|||
self.actionStaccato.setObjectName("actionStaccato") |
|||
self.actionCopy = QtWidgets.QAction(MainWindow) |
|||
self.actionCopy.setObjectName("actionCopy") |
|||
self.actionCut = QtWidgets.QAction(MainWindow) |
|||
self.actionCut.setObjectName("actionCut") |
|||
self.actionPaste = QtWidgets.QAction(MainWindow) |
|||
self.actionPaste.setObjectName("actionPaste") |
|||
self.actionUpOctave = QtWidgets.QAction(MainWindow) |
|||
self.actionUpOctave.setObjectName("actionUpOctave") |
|||
self.actionDownOctave = QtWidgets.QAction(MainWindow) |
|||
self.actionDownOctave.setObjectName("actionDownOctave") |
|||
self.actionDots = QtWidgets.QAction(MainWindow) |
|||
self.actionDots.setObjectName("actionDots") |
|||
self.actionCC_Mode = QtWidgets.QAction(MainWindow) |
|||
self.actionCC_Mode.setCheckable(True) |
|||
self.actionCC_Mode.setObjectName("actionCC_Mode") |
|||
self.CCactionSplit_Current_Block = QtWidgets.QAction(MainWindow) |
|||
self.CCactionSplit_Current_Block.setObjectName("CCactionSplit_Current_Block") |
|||
self.actionChange_Grid_Rhythm = QtWidgets.QAction(MainWindow) |
|||
self.actionChange_Grid_Rhythm.setCheckable(False) |
|||
self.actionChange_Grid_Rhythm.setChecked(False) |
|||
self.actionChange_Grid_Rhythm.setObjectName("actionChange_Grid_Rhythm") |
|||
self.actionSnap_To_Grid = QtWidgets.QAction(MainWindow) |
|||
self.actionSnap_To_Grid.setCheckable(True) |
|||
self.actionSnap_To_Grid.setChecked(True) |
|||
self.actionSnap_To_Grid.setObjectName("actionSnap_To_Grid") |
|||
self.actionAlt_modal1 = QtWidgets.QAction(MainWindow) |
|||
self.actionAlt_modal1.setObjectName("actionAlt_modal1") |
|||
self.actionAlt_modal2 = QtWidgets.QAction(MainWindow) |
|||
self.actionAlt_modal2.setObjectName("actionAlt_modal2") |
|||
self.actionAlt_modal3 = QtWidgets.QAction(MainWindow) |
|||
self.actionAlt_modal3.setObjectName("actionAlt_modal3") |
|||
self.actionAlt_modal4 = QtWidgets.QAction(MainWindow) |
|||
self.actionAlt_modal4.setObjectName("actionAlt_modal4") |
|||
self.actionAlt_modal5 = QtWidgets.QAction(MainWindow) |
|||
self.actionAlt_modal5.setObjectName("actionAlt_modal5") |
|||
self.actionAlt_modal6 = QtWidgets.QAction(MainWindow) |
|||
self.actionAlt_modal6.setObjectName("actionAlt_modal6") |
|||
self.actionAlt_modal7 = QtWidgets.QAction(MainWindow) |
|||
self.actionAlt_modal7.setObjectName("actionAlt_modal7") |
|||
self.actionAlt_modal8 = QtWidgets.QAction(MainWindow) |
|||
self.actionAlt_modal8.setObjectName("actionAlt_modal8") |
|||
self.actionAlt_modal9 = QtWidgets.QAction(MainWindow) |
|||
self.actionAlt_modal9.setObjectName("actionAlt_modal9") |
|||
self.actionAlt_modal0 = QtWidgets.QAction(MainWindow) |
|||
self.actionAlt_modal0.setObjectName("actionAlt_modal0") |
|||
self.actionMidi_In_is_Active = QtWidgets.QAction(MainWindow) |
|||
self.actionMidi_In_is_Active.setCheckable(True) |
|||
self.actionMidi_In_is_Active.setObjectName("actionMidi_In_is_Active") |
|||
self.actionTie = QtWidgets.QAction(MainWindow) |
|||
self.actionTie.setObjectName("actionTie") |
|||
self.actionLegatoSlur = QtWidgets.QAction(MainWindow) |
|||
self.actionLegatoSlur.setObjectName("actionLegatoSlur") |
|||
self.actionTenuto = QtWidgets.QAction(MainWindow) |
|||
self.actionTenuto.setObjectName("actionTenuto") |
|||
self.actionTriplet = QtWidgets.QAction(MainWindow) |
|||
self.actionTriplet.setObjectName("actionTriplet") |
|||
self.actionCustom_Tuplets = QtWidgets.QAction(MainWindow) |
|||
self.actionCustom_Tuplets.setEnabled(False) |
|||
self.actionCustom_Tuplets.setObjectName("actionCustom_Tuplets") |
|||
self.actionPrevailingRest = QtWidgets.QAction(MainWindow) |
|||
self.actionPrevailingRest.setEnabled(False) |
|||
self.actionPrevailingRest.setObjectName("actionPrevailingRest") |
|||
self.actionSplit_in_2 = QtWidgets.QAction(MainWindow) |
|||
self.actionSplit_in_2.setObjectName("actionSplit_in_2") |
|||
self.actionSplit_in_3 = QtWidgets.QAction(MainWindow) |
|||
self.actionSplit_in_3.setObjectName("actionSplit_in_3") |
|||
self.actionCustom_Split = QtWidgets.QAction(MainWindow) |
|||
self.actionCustom_Split.setEnabled(True) |
|||
self.actionCustom_Split.setObjectName("actionCustom_Split") |
|||
self.actionUndo = QtWidgets.QAction(MainWindow) |
|||
self.actionUndo.setObjectName("actionUndo") |
|||
self.actionRedo = QtWidgets.QAction(MainWindow) |
|||
self.actionRedo.setObjectName("actionRedo") |
|||
self.actionJoin_with_next_Block = QtWidgets.QAction(MainWindow) |
|||
self.actionJoin_with_next_Block.setObjectName("actionJoin_with_next_Block") |
|||
self.actionSet_UpBeat = QtWidgets.QAction(MainWindow) |
|||
self.actionSet_UpBeat.setObjectName("actionSet_UpBeat") |
|||
self.actionSelectLeft = QtWidgets.QAction(MainWindow) |
|||
self.actionSelectLeft.setObjectName("actionSelectLeft") |
|||
self.actionSelectRight = QtWidgets.QAction(MainWindow) |
|||
self.actionSelectRight.setObjectName("actionSelectRight") |
|||
self.actionSelectMeasureLeft = QtWidgets.QAction(MainWindow) |
|||
self.actionSelectMeasureLeft.setObjectName("actionSelectMeasureLeft") |
|||
self.actionSelectMeasureRight = QtWidgets.QAction(MainWindow) |
|||
self.actionSelectMeasureRight.setObjectName("actionSelectMeasureRight") |
|||
self.actionSelectHead = QtWidgets.QAction(MainWindow) |
|||
self.actionSelectHead.setObjectName("actionSelectHead") |
|||
self.actionSelectTail = QtWidgets.QAction(MainWindow) |
|||
self.actionSelectTail.setObjectName("actionSelectTail") |
|||
self.actionSelectTrackUp = QtWidgets.QAction(MainWindow) |
|||
self.actionSelectTrackUp.setObjectName("actionSelectTrackUp") |
|||
self.actionSelectTrackDown = QtWidgets.QAction(MainWindow) |
|||
self.actionSelectTrackDown.setObjectName("actionSelectTrackDown") |
|||
self.actionSelectTrackFirst = QtWidgets.QAction(MainWindow) |
|||
self.actionSelectTrackFirst.setObjectName("actionSelectTrackFirst") |
|||
self.actionSelectTrackLast = QtWidgets.QAction(MainWindow) |
|||
self.actionSelectTrackLast.setObjectName("actionSelectTrackLast") |
|||
self.actionSelectUp = QtWidgets.QAction(MainWindow) |
|||
self.actionSelectUp.setObjectName("actionSelectUp") |
|||
self.actionSelectDown = QtWidgets.QAction(MainWindow) |
|||
self.actionSelectDown.setObjectName("actionSelectDown") |
|||
self.actionSelectUpOctave = QtWidgets.QAction(MainWindow) |
|||
self.actionSelectUpOctave.setObjectName("actionSelectUpOctave") |
|||
self.actionSelectDownOctave = QtWidgets.QAction(MainWindow) |
|||
self.actionSelectDownOctave.setObjectName("actionSelectDownOctave") |
|||
self.actionSelectTrack = QtWidgets.QAction(MainWindow) |
|||
self.actionSelectTrack.setObjectName("actionSelectTrack") |
|||
self.actionSelectMeasureColumn = QtWidgets.QAction(MainWindow) |
|||
self.actionSelectMeasureColumn.setObjectName("actionSelectMeasureColumn") |
|||
self.actionSelectAllTracks = QtWidgets.QAction(MainWindow) |
|||
self.actionSelectAllTracks.setObjectName("actionSelectAllTracks") |
|||
self.actionVelocityMore = QtWidgets.QAction(MainWindow) |
|||
self.actionVelocityMore.setObjectName("actionVelocityMore") |
|||
self.actionVelocityLess = QtWidgets.QAction(MainWindow) |
|||
self.actionVelocityLess.setObjectName("actionVelocityLess") |
|||
self.actionDurationModMore = QtWidgets.QAction(MainWindow) |
|||
self.actionDurationModMore.setObjectName("actionDurationModMore") |
|||
self.actionDurationModLess = QtWidgets.QAction(MainWindow) |
|||
self.actionDurationModLess.setObjectName("actionDurationModLess") |
|||
self.actionReset_Velocity_Duration_Mod = QtWidgets.QAction(MainWindow) |
|||
self.actionReset_Velocity_Duration_Mod.setObjectName("actionReset_Velocity_Duration_Mod") |
|||
self.actionWiden_Score_View = QtWidgets.QAction(MainWindow) |
|||
self.actionWiden_Score_View.setObjectName("actionWiden_Score_View") |
|||
self.actionShrink_Score_View = QtWidgets.QAction(MainWindow) |
|||
self.actionShrink_Score_View.setObjectName("actionShrink_Score_View") |
|||
self.actionData_Editor = QtWidgets.QAction(MainWindow) |
|||
self.actionData_Editor.setCheckable(True) |
|||
self.actionData_Editor.setObjectName("actionData_Editor") |
|||
self.actionClef = QtWidgets.QAction(MainWindow) |
|||
self.actionClef.setObjectName("actionClef") |
|||
self.actionMetrical_Instruction = QtWidgets.QAction(MainWindow) |
|||
self.actionMetrical_Instruction.setObjectName("actionMetrical_Instruction") |
|||
self.actionKey_Signature = QtWidgets.QAction(MainWindow) |
|||
self.actionKey_Signature.setObjectName("actionKey_Signature") |
|||
self.actionDynamics = QtWidgets.QAction(MainWindow) |
|||
self.actionDynamics.setObjectName("actionDynamics") |
|||
self.actionMulti_Measure_Rest = QtWidgets.QAction(MainWindow) |
|||
self.actionMulti_Measure_Rest.setObjectName("actionMulti_Measure_Rest") |
|||
self.actionTemporary_Tempo_Change = QtWidgets.QAction(MainWindow) |
|||
self.actionTemporary_Tempo_Change.setObjectName("actionTemporary_Tempo_Change") |
|||
self.actionBeam = QtWidgets.QAction(MainWindow) |
|||
self.actionBeam.setObjectName("actionBeam") |
|||
self.actionRemoveBeams = QtWidgets.QAction(MainWindow) |
|||
self.actionRemoveBeams.setObjectName("actionRemoveBeams") |
|||
self.actionTempo_Change = QtWidgets.QAction(MainWindow) |
|||
self.actionTempo_Change.setObjectName("actionTempo_Change") |
|||
self.actionDelete_Tempo_Change = QtWidgets.QAction(MainWindow) |
|||
self.actionDelete_Tempo_Change.setObjectName("actionDelete_Tempo_Change") |
|||
self.actionBlock_Mode = QtWidgets.QAction(MainWindow) |
|||
self.actionBlock_Mode.setCheckable(True) |
|||
self.actionBlock_Mode.setObjectName("actionBlock_Mode") |
|||
self.actionNotation_Mode = QtWidgets.QAction(MainWindow) |
|||
self.actionNotation_Mode.setCheckable(True) |
|||
self.actionNotation_Mode.setChecked(True) |
|||
self.actionNotation_Mode.setObjectName("actionNotation_Mode") |
|||
self.actionTransposeChord = QtWidgets.QAction(MainWindow) |
|||
self.actionTransposeChord.setObjectName("actionTransposeChord") |
|||
self.actionTranspose_Score = QtWidgets.QAction(MainWindow) |
|||
self.actionTranspose_Score.setObjectName("actionTranspose_Score") |
|||
self.actionSave = QtWidgets.QAction(MainWindow) |
|||
self.actionSave.setObjectName("actionSave") |
|||
self.actionExport_File = QtWidgets.QAction(MainWindow) |
|||
self.actionExport_File.setObjectName("actionExport_File") |
|||
self.actionImport_File_replaces_current = QtWidgets.QAction(MainWindow) |
|||
self.actionImport_File_replaces_current.setObjectName("actionImport_File_replaces_current") |
|||
self.actionUnlink_Current_Block = QtWidgets.QAction(MainWindow) |
|||
self.actionUnlink_Current_Block.setObjectName("actionUnlink_Current_Block") |
|||
self.actionDelete_All_Empty_Blocks = QtWidgets.QAction(MainWindow) |
|||
self.actionDelete_All_Empty_Blocks.setObjectName("actionDelete_All_Empty_Blocks") |
|||
self.actionBlockRight = QtWidgets.QAction(MainWindow) |
|||
self.actionBlockRight.setObjectName("actionBlockRight") |
|||
self.actionBlockLeft = QtWidgets.QAction(MainWindow) |
|||
self.actionBlockLeft.setObjectName("actionBlockLeft") |
|||
self.actionSelectBlockRight = QtWidgets.QAction(MainWindow) |
|||
self.actionSelectBlockRight.setObjectName("actionSelectBlockRight") |
|||
self.actionSelectBlockLeft = QtWidgets.QAction(MainWindow) |
|||
self.actionSelectBlockLeft.setObjectName("actionSelectBlockLeft") |
|||
self.actionPedalNotes = QtWidgets.QAction(MainWindow) |
|||
self.actionPedalNotes.setObjectName("actionPedalNotes") |
|||
self.actionReverse = QtWidgets.QAction(MainWindow) |
|||
self.actionReverse.setObjectName("actionReverse") |
|||
self.actionDuplicateItem = QtWidgets.QAction(MainWindow) |
|||
self.actionDuplicateItem.setObjectName("actionDuplicateItem") |
|||
self.actionRandom = QtWidgets.QAction(MainWindow) |
|||
self.actionRandom.setObjectName("actionRandom") |
|||
self.actionAscending = QtWidgets.QAction(MainWindow) |
|||
self.actionAscending.setObjectName("actionAscending") |
|||
self.actionDescending = QtWidgets.QAction(MainWindow) |
|||
self.actionDescending.setObjectName("actionDescending") |
|||
self.actionMirror_around_Cursor = QtWidgets.QAction(MainWindow) |
|||
self.actionMirror_around_Cursor.setObjectName("actionMirror_around_Cursor") |
|||
self.actionInsert_Random = QtWidgets.QAction(MainWindow) |
|||
self.actionInsert_Random.setObjectName("actionInsert_Random") |
|||
self.actionRandom_chromatic_in_clef_range = QtWidgets.QAction(MainWindow) |
|||
self.actionRandom_chromatic_in_clef_range.setObjectName("actionRandom_chromatic_in_clef_range") |
|||
self.actionRandom_in_scale_in_clef_range = QtWidgets.QAction(MainWindow) |
|||
self.actionRandom_in_scale_in_clef_range.setObjectName("actionRandom_in_scale_in_clef_range") |
|||
self.actionRandom_pitch_from_clipboard = QtWidgets.QAction(MainWindow) |
|||
self.actionRandom_pitch_from_clipboard.setObjectName("actionRandom_pitch_from_clipboard") |
|||
self.actionProperties = QtWidgets.QAction(MainWindow) |
|||
self.actionProperties.setObjectName("actionProperties") |
|||
self.actionExport_to_Ly = QtWidgets.QAction(MainWindow) |
|||
self.actionExport_to_Ly.setObjectName("actionExport_to_Ly") |
|||
self.actionShow_PDF = QtWidgets.QAction(MainWindow) |
|||
self.actionShow_PDF.setObjectName("actionShow_PDF") |
|||
self.actionExport_to_pdf_file = QtWidgets.QAction(MainWindow) |
|||
self.actionExport_to_pdf_file.setObjectName("actionExport_to_pdf_file") |
|||
self.actionInstrument_Change = QtWidgets.QAction(MainWindow) |
|||
self.actionInstrument_Change.setObjectName("actionInstrument_Change") |
|||
self.actionProgram_Change_Relative = QtWidgets.QAction(MainWindow) |
|||
self.actionProgram_Change_Relative.setObjectName("actionProgram_Change_Relative") |
|||
self.actionBank_Change_Absolute = QtWidgets.QAction(MainWindow) |
|||
self.actionBank_Change_Absolute.setObjectName("actionBank_Change_Absolute") |
|||
self.actionBank_Change_Relative = QtWidgets.QAction(MainWindow) |
|||
self.actionBank_Change_Relative.setObjectName("actionBank_Change_Relative") |
|||
self.actionChannel_Change = QtWidgets.QAction(MainWindow) |
|||
self.actionChannel_Change.setObjectName("actionChannel_Change") |
|||
self.actionChannel_Change_Relative = QtWidgets.QAction(MainWindow) |
|||
self.actionChannel_Change_Relative.setObjectName("actionChannel_Change_Relative") |
|||
self.actionMIDI_Channel_Plus = QtWidgets.QAction(MainWindow) |
|||
self.actionMIDI_Channel_Plus.setObjectName("actionMIDI_Channel_Plus") |
|||
self.actionMIDI_Channel_Minus = QtWidgets.QAction(MainWindow) |
|||
self.actionMIDI_Channel_Minus.setObjectName("actionMIDI_Channel_Minus") |
|||
self.actionMIDI_Channel_Reset = QtWidgets.QAction(MainWindow) |
|||
self.actionMIDI_Channel_Reset.setObjectName("actionMIDI_Channel_Reset") |
|||
self.actionDelete_Current_Track = QtWidgets.QAction(MainWindow) |
|||
self.actionDelete_Current_Track.setObjectName("actionDelete_Current_Track") |
|||
self.actionUse_Current_Track_as_Metronome = QtWidgets.QAction(MainWindow) |
|||
self.actionUse_Current_Track_as_Metronome.setObjectName("actionUse_Current_Track_as_Metronome") |
|||
self.actionMetronome_Enabled = QtWidgets.QAction(MainWindow) |
|||
self.actionMetronome_Enabled.setCheckable(True) |
|||
self.actionMetronome_Enabled.setObjectName("actionMetronome_Enabled") |
|||
self.actionRandom_in_scale_in_cursor_plus_octave = QtWidgets.QAction(MainWindow) |
|||
self.actionRandom_in_scale_in_cursor_plus_octave.setObjectName("actionRandom_in_scale_in_cursor_plus_octave") |
|||
self.actionRandom_in_scale_in_octave_around_cursor = QtWidgets.QAction(MainWindow) |
|||
self.actionRandom_in_scale_in_octave_around_cursor.setObjectName("actionRandom_in_scale_in_octave_around_cursor") |
|||
self.actionCreate_pool_from_selection = QtWidgets.QAction(MainWindow) |
|||
self.actionCreate_pool_from_selection.setObjectName("actionCreate_pool_from_selection") |
|||
self.menuObjects.addAction(self.actionMetrical_Instruction) |
|||
self.menuObjects.addAction(self.actionClef) |
|||
self.menuObjects.addAction(self.actionKey_Signature) |
|||
self.menuObjects.addSeparator() |
|||
self.menuObjects.addAction(self.actionDynamics) |
|||
self.menuObjects.addAction(self.actionMulti_Measure_Rest) |
|||
self.menuObjects.addAction(self.actionLegatoSlur) |
|||
self.menuObjects.addSeparator() |
|||
self.menuObjects.addAction(self.actionTempo_Change) |
|||
self.menuObjects.addAction(self.actionDelete_Tempo_Change) |
|||
self.menuObjects.addAction(self.actionTemporary_Tempo_Change) |
|||
self.menuObjects.addSeparator() |
|||
self.menuObjects.addAction(self.actionInstrument_Change) |
|||
self.menuGeneric.addAction(self.actionBlockLeft) |
|||
self.menuGeneric.addAction(self.actionBlockRight) |
|||
self.menuGeneric.addAction(self.actionSelectBlockRight) |
|||
self.menuGeneric.addAction(self.actionSelectBlockLeft) |
|||
self.menuGeneric.addAction(self.actionDownOctave) |
|||
self.menuGeneric.addAction(self.actionAlt_modal1) |
|||
self.menuGeneric.addAction(self.actionAlt_modal2) |
|||
self.menuGeneric.addAction(self.actionUpOctave) |
|||
self.menuGeneric.addAction(self.actionUp) |
|||
self.menuGeneric.addAction(self.actionDown) |
|||
self.menuGeneric.addAction(self.actionLeft) |
|||
self.menuGeneric.addAction(self.actionRight) |
|||
self.menuGeneric.addAction(self.actionAlt_modal3) |
|||
self.menuGeneric.addAction(self.actionAlt_modal4) |
|||
self.menuGeneric.addAction(self.actionAlt_modal5) |
|||
self.menuGeneric.addAction(self.actionAlt_modal6) |
|||
self.menuGeneric.addAction(self.actionAlt_modal7) |
|||
self.menuGeneric.addAction(self.actionAlt_modal8) |
|||
self.menuGeneric.addAction(self.actionAlt_modal9) |
|||
self.menuGeneric.addAction(self.actionAlt_modal0) |
|||
self.menuGeneric.addAction(self.actionModal1) |
|||
self.menuGeneric.addAction(self.actionModal2) |
|||
self.menuGeneric.addAction(self.actionModal3) |
|||
self.menuGeneric.addAction(self.actionModal4) |
|||
self.menuGeneric.addAction(self.actionModal5) |
|||
self.menuGeneric.addAction(self.actionModal6) |
|||
self.menuGeneric.addAction(self.actionModal7) |
|||
self.menuGeneric.addAction(self.actionModal8) |
|||
self.menuGeneric.addAction(self.actionModal9) |
|||
self.menuGeneric.addAction(self.actionModal0) |
|||
self.menuGeneric.addAction(self.actionShift_modal1) |
|||
self.menuGeneric.addAction(self.actionShift_modal2) |
|||
self.menuGeneric.addAction(self.actionShift_modal3) |
|||
self.menuGeneric.addAction(self.actionShift_modal4) |
|||
self.menuGeneric.addAction(self.actionShift_modal5) |
|||
self.menuGeneric.addAction(self.actionShift_modal6) |
|||
self.menuGeneric.addAction(self.actionShift_modal7) |
|||
self.menuGeneric.addAction(self.actionShift_modal8) |
|||
self.menuGeneric.addAction(self.actionShift_modal9) |
|||
self.menuGeneric.addAction(self.actionShift_modal0) |
|||
self.menuGeneric.addAction(self.actionTrackUp) |
|||
self.menuGeneric.addAction(self.actionTrackDown) |
|||
self.menuGeneric.addAction(self.actionHead) |
|||
self.menuGeneric.addAction(self.actionTail) |
|||
self.menuGeneric.addAction(self.actionTrackFirst) |
|||
self.menuGeneric.addAction(self.actionMeasureRight) |
|||
self.menuGeneric.addAction(self.actionTrackLast) |
|||
self.menuGeneric.addAction(self.actionMeasureLeft) |
|||
self.menuGeneric.addAction(self.actionSelectLeft) |
|||
self.menuGeneric.addAction(self.actionSelectRight) |
|||
self.menuGeneric.addAction(self.actionSelectMeasureLeft) |
|||
self.menuGeneric.addAction(self.actionSelectMeasureRight) |
|||
self.menuGeneric.addAction(self.actionSelectHead) |
|||
self.menuGeneric.addAction(self.actionSelectTail) |
|||
self.menuGeneric.addAction(self.actionSelectTrackUp) |
|||
self.menuGeneric.addAction(self.actionSelectTrackDown) |
|||
self.menuGeneric.addAction(self.actionSelectTrackFirst) |
|||
self.menuGeneric.addAction(self.actionSelectTrackLast) |
|||
self.menuGeneric.addAction(self.actionSelectUp) |
|||
self.menuGeneric.addAction(self.actionSelectDown) |
|||
self.menuGeneric.addAction(self.actionSelectUpOctave) |
|||
self.menuGeneric.addAction(self.actionSelectDownOctave) |
|||
self.menuGeneric.addAction(self.actionSelectTrack) |
|||
self.menuGeneric.addAction(self.actionSelectAllTracks) |
|||
self.menuGeneric.addAction(self.actionSelectMeasureColumn) |
|||
self.menuGeneric.addAction(self.menuType_Here.menuAction()) |
|||
self.menuTracks.addAction(self.actionAdd_Track) |
|||
self.menuTracks.addAction(self.actionDelete_Current_Track) |
|||
self.menuTracks.addAction(self.actionUse_Current_Track_as_Metronome) |
|||
self.menuTracks.addSeparator() |
|||
self.menuTracks.addAction(self.actionSplit_Current_Block) |
|||
self.menuTracks.addAction(self.actionAppend_Block) |
|||
self.menuTracks.addAction(self.actionDuplicate) |
|||
self.menuTracks.addAction(self.actionUnlink_Current_Block) |
|||
self.menuTracks.addAction(self.actionCreate_Linked_Copy) |
|||
self.menuTracks.addAction(self.actionDelete_Current_Block) |
|||
self.menuTracks.addAction(self.actionJoin_with_next_Block) |
|||
self.menuTracks.addSeparator() |
|||
self.menuTracks.addAction(self.actionTranspose_Score) |
|||
self.menuTracks.addAction(self.actionDelete_All_Empty_Blocks) |
|||
self.menuEdit.addAction(self.actionSharpen_Note) |
|||
self.menuEdit.addAction(self.actionFlatten_Note) |
|||
self.menuEdit.addAction(self.actionStep_Up) |
|||
self.menuEdit.addAction(self.actionStep_Down) |
|||
self.menuEdit.addAction(self.actionOctave_Up) |
|||
self.menuEdit.addAction(self.actionOctave_Down) |
|||
self.menuEdit.addAction(self.actionTransposeChord) |
|||
self.menuEdit.addSeparator() |
|||
self.menuEdit.addAction(self.actionAugment) |
|||
self.menuEdit.addAction(self.actionDiminish) |
|||
self.menuEdit.addAction(self.actionDots) |
|||
self.menuEdit.addAction(self.actionTriplet) |
|||
self.menuEdit.addAction(self.actionCustom_Tuplets) |
|||
self.menuEdit.addSeparator() |
|||
self.menuEdit.addAction(self.actionStaccato) |
|||
self.menuEdit.addAction(self.actionTenuto) |
|||
self.menuEdit.addAction(self.actionTie) |
|||
self.menuEdit.addSeparator() |
|||
self.menuEdit.addAction(self.actionVelocityMore) |
|||
self.menuEdit.addAction(self.actionVelocityLess) |
|||
self.menuEdit.addAction(self.actionDurationModMore) |
|||
self.menuEdit.addAction(self.actionDurationModLess) |
|||
self.menuEdit.addAction(self.actionReset_Velocity_Duration_Mod) |
|||
self.menuMode.addAction(self.actionNotation_Mode) |
|||
self.menuMode.addAction(self.actionBlock_Mode) |
|||
self.menuMode.addAction(self.actionCC_Mode) |
|||
self.menuView.addAction(self.actionSave) |
|||
self.menuView.addSeparator() |
|||
self.menuView.addAction(self.actionToggle_Notehead_Rectangles) |
|||
self.menuView.addAction(self.actionChange_Grid_Rhythm) |
|||
self.menuView.addAction(self.actionFollow_Playhead) |
|||
self.menuView.addAction(self.actionMidi_In_is_Active) |
|||
self.menuView.addAction(self.menuMode.menuAction()) |
|||
self.menuView.addSeparator() |
|||
self.menuView.addAction(self.actionData_Editor) |
|||
self.menuView.addAction(self.actionProperties) |
|||
self.menuView.addAction(self.actionWiden_Score_View) |
|||
self.menuView.addAction(self.actionShrink_Score_View) |
|||
self.menuView.addSeparator() |
|||
self.menuView.addAction(self.actionPlayPause) |
|||
self.menuView.addAction(self.actionPlayFromEditCursor) |
|||
self.menuView.addAction(self.actionPlayFromBeginning) |
|||
self.menuView.addAction(self.actionMetronome_Enabled) |
|||
self.menuView.addSeparator() |
|||
self.menuEdit_MusicItem.addAction(self.actionAddCursorNoteToChord) |
|||
self.menuEdit_MusicItem.addAction(self.actionDeleteCursorNoteFromChord) |
|||
self.menuEdit_MusicItem.addAction(self.actionSplit_in_2) |
|||
self.menuEdit_MusicItem.addAction(self.actionSplit_in_3) |
|||
self.menuEdit_MusicItem.addAction(self.actionCustom_Split) |
|||
self.menuEdit_MusicItem.addSeparator() |
|||
self.menuEdit_MusicItem.addAction(self.actionBeam) |
|||
self.menuEdit_MusicItem.addAction(self.actionRemoveBeams) |
|||
self.menuEdit_MusicItem.addSeparator() |
|||
self.menuEdit_MusicItem.addAction(self.actionMIDI_Channel_Plus) |
|||
self.menuEdit_MusicItem.addAction(self.actionMIDI_Channel_Minus) |
|||
self.menuEdit_MusicItem.addAction(self.actionMIDI_Channel_Reset) |
|||
self.menuEdit_2.addAction(self.actionDelete) |
|||
self.menuEdit_2.addAction(self.actionBackspace) |
|||
self.menuEdit_2.addAction(self.actionUndo) |
|||
self.menuEdit_2.addAction(self.actionRedo) |
|||
self.menuEdit_2.addAction(self.actionCopy) |
|||
self.menuEdit_2.addAction(self.actionPaste) |
|||
self.menuEdit_2.addAction(self.actionCut) |
|||
self.menuEdit_2.addAction(self.actionDuplicateItem) |
|||
self.menuOrder.addAction(self.actionRandom) |
|||
self.menuOrder.addAction(self.actionReverse) |
|||
self.menuOrder.addAction(self.actionAscending) |
|||
self.menuOrder.addAction(self.actionDescending) |
|||
self.menuInsert.addAction(self.actionRandom_chromatic_in_clef_range) |
|||
self.menuInsert.addAction(self.actionRandom_in_scale_in_clef_range) |
|||
self.menuInsert.addAction(self.actionRandom_in_scale_in_cursor_plus_octave) |
|||
self.menuInsert.addAction(self.actionRandom_in_scale_in_octave_around_cursor) |
|||
self.menuInsert.addAction(self.actionRandom_pitch_from_clipboard) |
|||
self.menuInsert.addSeparator() |
|||
self.menuInsert.addAction(self.actionPedalNotes) |
|||
self.menuInsert.addAction(self.actionMirror_around_Cursor) |
|||
self.menuMIDI.addAction(self.actionChannel_Change) |
|||
self.menuToolbox.addAction(self.menuInsert.menuAction()) |
|||
self.menuToolbox.addAction(self.menuOrder.menuAction()) |
|||
self.menuToolbox.addAction(self.menuMIDI.menuAction()) |
|||
self.menuLilypond.addAction(self.actionShow_PDF) |
|||
self.menuLilypond.addAction(self.actionExport_to_Ly) |
|||
self.menubar.addAction(self.menuView.menuAction()) |
|||
self.menubar.addAction(self.menuEdit_2.menuAction()) |
|||
self.menubar.addAction(self.menu.menuAction()) |
|||
self.menubar.addAction(self.menuTracks.menuAction()) |
|||
self.menubar.addAction(self.menuObjects.menuAction()) |
|||
self.menubar.addAction(self.menuEdit_MusicItem.menuAction()) |
|||
self.menubar.addAction(self.menuEdit.menuAction()) |
|||
self.menubar.addAction(self.menuToolbox.menuAction()) |
|||
self.menubar.addAction(self.menu_2.menuAction()) |
|||
self.menubar.addAction(self.menuLilypond.menuAction()) |
|||
self.menubar.addAction(self.menuHelp.menuAction()) |
|||
self.menubar.addAction(self.menuGeneric.menuAction()) |
|||
|
|||
self.retranslateUi(MainWindow) |
|||
QtCore.QMetaObject.connectSlotsByName(MainWindow) |
|||
|
|||
def retranslateUi(self, MainWindow): |
|||
_translate = QtCore.QCoreApplication.translate |
|||
MainWindow.setWindowTitle(_translate("MainWindow", "Laborejo")) |
|||
self.toolBar.setWindowTitle(_translate("MainWindow", "toolBar")) |
|||
self.menuHelp.setTitle(_translate("MainWindow", "Help")) |
|||
self.menuObjects.setTitle(_translate("MainWindow", "&Insert Objects")) |
|||
self.menuGeneric.setTitle(_translate("MainWindow", "&Generic")) |
|||
self.menuType_Here.setTitle(_translate("MainWindow", "T&ype Here")) |
|||
self.menuTracks.setTitle(_translate("MainWindow", "&Structures")) |
|||
self.menuEdit.setTitle(_translate("MainWindow", "E&dit Note")) |
|||
self.menuView.setTitle(_translate("MainWindow", "&Control")) |
|||
self.menuMode.setTitle(_translate("MainWindow", "&View and Edit Mode")) |
|||
self.menu.setTitle(_translate("MainWindow", "|")) |
|||
self.menu_2.setTitle(_translate("MainWindow", "|")) |
|||
self.menuEdit_MusicItem.setTitle(_translate("MainWindow", "Edit &MusicItem")) |
|||
self.menuEdit_2.setTitle(_translate("MainWindow", "E&dit")) |
|||
self.menuToolbox.setTitle(_translate("MainWindow", "&Toolbox")) |
|||
self.menuOrder.setTitle(_translate("MainWindow", "Note &Sorting")) |
|||
self.menuInsert.setTitle(_translate("MainWindow", "&Note Generation")) |
|||
self.menuMIDI.setTitle(_translate("MainWindow", "&MIDI")) |
|||
self.menuLilypond.setTitle(_translate("MainWindow", "&Lilypond")) |
|||
self.leftToolBar.setWindowTitle(_translate("MainWindow", "leftToolBar")) |
|||
self.actionUp.setText(_translate("MainWindow", "u&p")) |
|||
self.actionUp.setShortcut(_translate("MainWindow", "Up")) |
|||
self.actionDown.setText(_translate("MainWindow", "d&own")) |
|||
self.actionDown.setShortcut(_translate("MainWindow", "Down")) |
|||
self.actionLeft.setText(_translate("MainWindow", "&left")) |
|||
self.actionLeft.setShortcut(_translate("MainWindow", "Left")) |
|||
self.actionRight.setText(_translate("MainWindow", "&right")) |
|||
self.actionRight.setShortcut(_translate("MainWindow", "Right")) |
|||
self.actionModal1.setText(_translate("MainWindow", "&modal1")) |
|||
self.actionModal1.setShortcut(_translate("MainWindow", "1")) |
|||
self.actionModal2.setText(_translate("MainWindow", "modal&2")) |
|||
self.actionModal2.setShortcut(_translate("MainWindow", "2")) |
|||
self.actionModal3.setText(_translate("MainWindow", "modal&3")) |
|||
self.actionModal3.setShortcut(_translate("MainWindow", "3")) |
|||
self.actionModal4.setText(_translate("MainWindow", "modal&4")) |
|||
self.actionModal4.setShortcut(_translate("MainWindow", "4")) |
|||
self.actionModal5.setText(_translate("MainWindow", "modal&5")) |
|||
self.actionModal5.setShortcut(_translate("MainWindow", "5")) |
|||
self.actionModal6.setText(_translate("MainWindow", "modal&6")) |
|||
self.actionModal6.setShortcut(_translate("MainWindow", "6")) |
|||
self.actionModal7.setText(_translate("MainWindow", "modal&7")) |
|||
self.actionModal7.setShortcut(_translate("MainWindow", "7")) |
|||
self.actionModal8.setText(_translate("MainWindow", "modal&8")) |
|||
self.actionModal8.setShortcut(_translate("MainWindow", "8")) |
|||
self.actionModal9.setText(_translate("MainWindow", "modal&9")) |
|||
self.actionModal9.setShortcut(_translate("MainWindow", "9")) |
|||
self.actionModal0.setText(_translate("MainWindow", "modal&0")) |
|||
self.actionModal0.setShortcut(_translate("MainWindow", "0")) |
|||
self.actionDelete.setText(_translate("MainWindow", "&Delete Item")) |
|||
self.actionDelete.setShortcut(_translate("MainWindow", "Del")) |
|||
self.actionBackspace.setText(_translate("MainWindow", "Delete previous &Item")) |
|||
self.actionBackspace.setShortcut(_translate("MainWindow", "Backspace")) |
|||
self.actionShift_modal1.setText(_translate("MainWindow", "sh&ift+modal1")) |
|||
self.actionShift_modal1.setShortcut(_translate("MainWindow", "Shift+1")) |
|||
self.actionShift_modal2.setText(_translate("MainWindow", "shi&ft+modal2")) |
|||
self.actionShift_modal2.setShortcut(_translate("MainWindow", "Shift+2")) |
|||
self.actionShift_modal3.setText(_translate("MainWindow", "shift+modal3")) |
|||
self.actionShift_modal3.setShortcut(_translate("MainWindow", "Shift+3")) |
|||
self.actionShift_modal4.setText(_translate("MainWindow", "shift+modal4")) |
|||
self.actionShift_modal4.setShortcut(_translate("MainWindow", "Shift+4")) |
|||
self.actionShift_modal5.setText(_translate("MainWindow", "shift+modal5")) |
|||
self.actionShift_modal5.setShortcut(_translate("MainWindow", "Shift+5")) |
|||
self.actionShift_modal6.setText(_translate("MainWindow", "shift+modal6")) |
|||
self.actionShift_modal6.setShortcut(_translate("MainWindow", "Shift+6")) |
|||
self.actionShift_modal7.setText(_translate("MainWindow", "shift+modal7")) |
|||
self.actionShift_modal7.setShortcut(_translate("MainWindow", "Shift+7")) |
|||
self.actionShift_modal8.setText(_translate("MainWindow", "shift+modal8")) |
|||
self.actionShift_modal8.setShortcut(_translate("MainWindow", "Shift+8")) |
|||
self.actionShift_modal9.setText(_translate("MainWindow", "shift+modal9")) |
|||
self.actionShift_modal9.setShortcut(_translate("MainWindow", "Shift+9")) |
|||
self.actionShift_modal0.setText(_translate("MainWindow", "shift+modal0")) |
|||
self.actionShift_modal0.setShortcut(_translate("MainWindow", "Shift+0")) |
|||
self.actionAddCursorNoteToChord.setText(_translate("MainWindow", "&Add Note to Chord")) |
|||
self.actionAddCursorNoteToChord.setShortcut(_translate("MainWindow", "Return")) |
|||
self.actionDeleteCursorNoteFromChord.setText(_translate("MainWindow", "&Delete Note from Chord")) |
|||
self.actionDeleteCursorNoteFromChord.setShortcut(_translate("MainWindow", "Shift+Del")) |
|||
self.actionAdd_Track.setText(_translate("MainWindow", "&Add Track")) |
|||
self.actionTrackUp.setText(_translate("MainWindow", "&trackUp")) |
|||
self.actionTrackUp.setShortcut(_translate("MainWindow", "PgUp")) |
|||
self.actionTrackDown.setText(_translate("MainWindow", "trac&kDown")) |
|||
self.actionTrackDown.setShortcut(_translate("MainWindow", "PgDown")) |
|||
self.actionHead.setText(_translate("MainWindow", "&head")) |
|||
self.actionHead.setShortcut(_translate("MainWindow", "Home")) |
|||
self.actionTail.setText(_translate("MainWindow", "tail")) |
|||
self.actionTail.setShortcut(_translate("MainWindow", "End")) |
|||
self.actionTrackFirst.setText(_translate("MainWindow", "trackFirst")) |
|||
self.actionTrackFirst.setShortcut(_translate("MainWindow", "Ctrl+PgUp")) |
|||
self.actionTrackLast.setText(_translate("MainWindow", "trackLast")) |
|||
self.actionTrackLast.setShortcut(_translate("MainWindow", "Ctrl+PgDown")) |
|||
self.actionSharpen_Note.setText(_translate("MainWindow", "&Sharpen Note")) |
|||
self.actionSharpen_Note.setShortcut(_translate("MainWindow", "+")) |
|||
self.actionFlatten_Note.setText(_translate("MainWindow", "&Flatten Note")) |
|||
self.actionFlatten_Note.setShortcut(_translate("MainWindow", "-")) |
|||
self.actionMeasureLeft.setText(_translate("MainWindow", "measureLeft")) |
|||
self.actionMeasureLeft.setShortcut(_translate("MainWindow", "Ctrl+Left")) |
|||
self.actionMeasureRight.setText(_translate("MainWindow", "measureRight")) |
|||
self.actionMeasureRight.setShortcut(_translate("MainWindow", "Ctrl+Right")) |
|||
self.actionStep_Up.setText(_translate("MainWindow", "Step &Up")) |
|||
self.actionStep_Up.setShortcut(_translate("MainWindow", "Alt+Up")) |
|||
self.actionStep_Down.setText(_translate("MainWindow", "Ste&p Down")) |
|||
self.actionStep_Down.setShortcut(_translate("MainWindow", "Alt+Down")) |
|||
self.actionOctave_Up.setText(_translate("MainWindow", "&Octave Up")) |
|||
self.actionOctave_Up.setShortcut(_translate("MainWindow", "Ctrl+Alt+Up")) |
|||
self.actionOctave_Down.setText(_translate("MainWindow", "Octa&ve Down")) |
|||
self.actionOctave_Down.setShortcut(_translate("MainWindow", "Ctrl+Alt+Down")) |
|||
self.actionAugment.setText(_translate("MainWindow", "&Augment")) |
|||
self.actionAugment.setShortcut(_translate("MainWindow", "Alt+Right")) |
|||
self.actionDiminish.setText(_translate("MainWindow", "&Diminish")) |
|||
self.actionDiminish.setShortcut(_translate("MainWindow", "Alt+Left")) |
|||
self.actionAppend_Block.setText(_translate("MainWindow", "Append &Block")) |
|||
self.actionSplit_Current_Block.setText(_translate("MainWindow", "&Split Current Block")) |
|||
self.actionSplit_Current_Block.setShortcut(_translate("MainWindow", "Ins")) |
|||
self.actionDuplicate.setText(_translate("MainWindow", "&Duplicate Current Block")) |
|||
self.actionDuplicate.setShortcut(_translate("MainWindow", "Alt+Ins")) |
|||
self.actionCreate_Linked_Copy.setText(_translate("MainWindow", "&Create Linked Copy")) |
|||
self.actionCreate_Linked_Copy.setShortcut(_translate("MainWindow", "Alt+Shift+Ins")) |
|||
self.actionDelete_Current_Block.setText(_translate("MainWindow", "De&lete Current Block")) |
|||
self.actionToggle_Notehead_Rectangles.setText(_translate("MainWindow", "&Rectangle Noteheads")) |
|||
self.actionToggle_Notehead_Rectangles.setShortcut(_translate("MainWindow", "F1")) |
|||
self.actionPlayPause.setText(_translate("MainWindow", "P&lay / Pause")) |
|||
self.actionPlayPause.setShortcut(_translate("MainWindow", "Space")) |
|||
self.actionPlayFromBeginning.setText(_translate("MainWindow", "Play fr&om Start")) |
|||
self.actionPlayFromBeginning.setShortcut(_translate("MainWindow", "Shift+Space")) |
|||
self.actionFollow_Playhead.setText(_translate("MainWindow", "&Follow Playhead")) |
|||
self.actionFollow_Playhead.setShortcut(_translate("MainWindow", "F3")) |
|||
self.actionPlayFromEditCursor.setText(_translate("MainWindow", "Pla&y from Cursor")) |
|||
self.actionPlayFromEditCursor.setShortcut(_translate("MainWindow", "Ctrl+Space")) |
|||
self.actionStaccato.setText(_translate("MainWindow", "Staccato")) |
|||
self.actionStaccato.setShortcut(_translate("MainWindow", ",")) |
|||
self.actionCopy.setText(_translate("MainWindow", "&Copy")) |
|||
self.actionCopy.setShortcut(_translate("MainWindow", "Ctrl+C")) |
|||
self.actionCut.setText(_translate("MainWindow", "Cut")) |
|||
self.actionCut.setShortcut(_translate("MainWindow", "Ctrl+X")) |
|||
self.actionPaste.setText(_translate("MainWindow", "&Paste")) |
|||
self.actionPaste.setShortcut(_translate("MainWindow", "Ctrl+V")) |
|||
self.actionUpOctave.setText(_translate("MainWindow", "&upOctave")) |
|||
self.actionUpOctave.setShortcut(_translate("MainWindow", "Ctrl+Up")) |
|||
self.actionDownOctave.setText(_translate("MainWindow", "&downOctave")) |
|||
self.actionDownOctave.setShortcut(_translate("MainWindow", "Ctrl+Down")) |
|||
self.actionDots.setText(_translate("MainWindow", "Dots")) |
|||
self.actionDots.setShortcut(_translate("MainWindow", "Q")) |
|||
self.actionCC_Mode.setText(_translate("MainWindow", "&Control Changes")) |
|||
self.actionCC_Mode.setShortcut(_translate("MainWindow", "F7")) |
|||
self.CCactionSplit_Current_Block.setText(_translate("MainWindow", "CCactionSplit_Current_Block")) |
|||
self.CCactionSplit_Current_Block.setShortcut(_translate("MainWindow", "Ins")) |
|||
self.actionChange_Grid_Rhythm.setText(_translate("MainWindow", "&Change Grid Rhythm")) |
|||
self.actionChange_Grid_Rhythm.setShortcut(_translate("MainWindow", "F2")) |
|||
self.actionSnap_To_Grid.setText(_translate("MainWindow", "Snap To Grid")) |
|||
self.actionAlt_modal1.setText(_translate("MainWindow", "&Alt_modal1")) |
|||
self.actionAlt_modal1.setShortcut(_translate("MainWindow", "Alt+1")) |
|||
self.actionAlt_modal2.setText(_translate("MainWindow", "Alt_modal2")) |
|||
self.actionAlt_modal2.setShortcut(_translate("MainWindow", "Alt+2")) |
|||
self.actionAlt_modal3.setText(_translate("MainWindow", "Alt_modal3")) |
|||
self.actionAlt_modal3.setShortcut(_translate("MainWindow", "Alt+3")) |
|||
self.actionAlt_modal4.setText(_translate("MainWindow", "Alt_modal4")) |
|||
self.actionAlt_modal4.setShortcut(_translate("MainWindow", "Alt+4")) |
|||
self.actionAlt_modal5.setText(_translate("MainWindow", "Alt_modal5")) |
|||
self.actionAlt_modal5.setShortcut(_translate("MainWindow", "Alt+5")) |
|||
self.actionAlt_modal6.setText(_translate("MainWindow", "Alt_modal6")) |
|||
self.actionAlt_modal6.setShortcut(_translate("MainWindow", "Alt+6")) |
|||
self.actionAlt_modal7.setText(_translate("MainWindow", "Alt_modal7")) |
|||
self.actionAlt_modal7.setShortcut(_translate("MainWindow", "Alt+7")) |
|||
self.actionAlt_modal8.setText(_translate("MainWindow", "Alt_modal8")) |
|||
self.actionAlt_modal8.setShortcut(_translate("MainWindow", "Alt+8")) |
|||
self.actionAlt_modal9.setText(_translate("MainWindow", "Alt_modal9")) |
|||
self.actionAlt_modal9.setShortcut(_translate("MainWindow", "Alt+9")) |
|||
self.actionAlt_modal0.setText(_translate("MainWindow", "Alt_modal0")) |
|||
self.actionAlt_modal0.setShortcut(_translate("MainWindow", "Alt+0")) |
|||
self.actionMidi_In_is_Active.setText(_translate("MainWindow", "&Midi In is Active")) |
|||
self.actionMidi_In_is_Active.setToolTip(_translate("MainWindow", "Toggle midi in on and off")) |
|||
self.actionMidi_In_is_Active.setShortcut(_translate("MainWindow", "F4")) |
|||
self.actionTie.setText(_translate("MainWindow", "T&ie")) |
|||
self.actionTie.setShortcut(_translate("MainWindow", "I")) |
|||
self.actionLegatoSlur.setText(_translate("MainWindow", "&Legato Slur")) |
|||
self.actionLegatoSlur.setToolTip(_translate("MainWindow", "Toggle Legato Slur")) |
|||
self.actionLegatoSlur.setShortcut(_translate("MainWindow", "S")) |
|||
self.actionTenuto.setText(_translate("MainWindow", "T&enuto")) |
|||
self.actionTenuto.setShortcut(_translate("MainWindow", "E")) |
|||
self.actionTriplet.setText(_translate("MainWindow", "Triplet")) |
|||
self.actionTriplet.setShortcut(_translate("MainWindow", "T")) |
|||
self.actionCustom_Tuplets.setText(_translate("MainWindow", "&Custom Tuplets")) |
|||
self.actionCustom_Tuplets.setShortcut(_translate("MainWindow", "Alt+T")) |
|||
self.actionPrevailingRest.setText(_translate("MainWindow", "Prevailing Rest")) |
|||
self.actionSplit_in_2.setText(_translate("MainWindow", "&Split in 2")) |
|||
self.actionSplit_in_2.setShortcut(_translate("MainWindow", "X")) |
|||
self.actionSplit_in_3.setText(_translate("MainWindow", "Split &in 3")) |
|||
self.actionSplit_in_3.setShortcut(_translate("MainWindow", "Shift+X")) |
|||
self.actionCustom_Split.setText(_translate("MainWindow", "Split in &X")) |
|||
self.actionCustom_Split.setShortcut(_translate("MainWindow", "Alt+X")) |
|||
self.actionUndo.setText(_translate("MainWindow", "&Undo")) |
|||
self.actionUndo.setShortcut(_translate("MainWindow", "Ctrl+Z")) |
|||
self.actionRedo.setText(_translate("MainWindow", "&Redo")) |
|||
self.actionRedo.setShortcut(_translate("MainWindow", "Ctrl+Shift+Z")) |
|||
self.actionJoin_with_next_Block.setText(_translate("MainWindow", "&Join with next Block")) |
|||
self.actionJoin_with_next_Block.setShortcut(_translate("MainWindow", "Shift+Ins")) |
|||
self.actionSet_UpBeat.setText(_translate("MainWindow", "Set Upbeat")) |
|||
self.actionSelectLeft.setText(_translate("MainWindow", "selectLeft")) |
|||
self.actionSelectLeft.setShortcut(_translate("MainWindow", "Shift+Left")) |
|||
self.actionSelectRight.setText(_translate("MainWindow", "selectRi&ght")) |
|||
self.actionSelectRight.setShortcut(_translate("MainWindow", "Shift+Right")) |
|||
self.actionSelectMeasureLeft.setText(_translate("MainWindow", "selectMeasureLeft")) |
|||
self.actionSelectMeasureLeft.setShortcut(_translate("MainWindow", "Ctrl+Shift+Left")) |
|||
self.actionSelectMeasureRight.setText(_translate("MainWindow", "selectMeasureRight")) |
|||
self.actionSelectMeasureRight.setShortcut(_translate("MainWindow", "Ctrl+Shift+Right")) |
|||
self.actionSelectHead.setText(_translate("MainWindow", "selectHead")) |
|||
self.actionSelectHead.setShortcut(_translate("MainWindow", "Shift+Home")) |
|||
self.actionSelectTail.setText(_translate("MainWindow", "selectTail")) |
|||
self.actionSelectTail.setShortcut(_translate("MainWindow", "Shift+End")) |
|||
self.actionSelectTrackUp.setText(_translate("MainWindow", "selectTrackUp")) |
|||
self.actionSelectTrackUp.setShortcut(_translate("MainWindow", "Shift+PgUp")) |
|||
self.actionSelectTrackDown.setText(_translate("MainWindow", "selectTrackDown")) |
|||
self.actionSelectTrackDown.setShortcut(_translate("MainWindow", "Shift+PgDown")) |
|||
self.actionSelectTrackFirst.setText(_translate("MainWindow", "selectTrackFirst")) |
|||
self.actionSelectTrackFirst.setShortcut(_translate("MainWindow", "Ctrl+Shift+PgUp")) |
|||
self.actionSelectTrackLast.setText(_translate("MainWindow", "selectTrackLast")) |
|||
self.actionSelectTrackLast.setShortcut(_translate("MainWindow", "Ctrl+Shift+PgDown")) |
|||
self.actionSelectUp.setText(_translate("MainWindow", "selectUp")) |
|||
self.actionSelectUp.setShortcut(_translate("MainWindow", "Shift+Up")) |
|||
self.actionSelectDown.setText(_translate("MainWindow", "selectDo&wn")) |
|||
self.actionSelectDown.setShortcut(_translate("MainWindow", "Shift+Down")) |
|||
self.actionSelectUpOctave.setText(_translate("MainWindow", "selectUpOcta&ve")) |
|||
self.actionSelectUpOctave.setShortcut(_translate("MainWindow", "Ctrl+Shift+Up")) |
|||
self.actionSelectDownOctave.setText(_translate("MainWindow", "selectDow&nOctave")) |
|||
self.actionSelectDownOctave.setShortcut(_translate("MainWindow", "Ctrl+Shift+Down")) |
|||
self.actionSelectTrack.setText(_translate("MainWindow", "selectTrack")) |
|||
self.actionSelectTrack.setShortcut(_translate("MainWindow", "Shift+A")) |
|||
self.actionSelectMeasureColumn.setText(_translate("MainWindow", "selectMeasureColumn")) |
|||
self.actionSelectMeasureColumn.setShortcut(_translate("MainWindow", "Ctrl+A")) |
|||
self.actionSelectAllTracks.setText(_translate("MainWindow", "selectAllTracks")) |
|||
self.actionSelectAllTracks.setShortcut(_translate("MainWindow", "Ctrl+Shift+A")) |
|||
self.actionVelocityMore.setText(_translate("MainWindow", "&More Velocity")) |
|||
self.actionVelocityMore.setToolTip(_translate("MainWindow", "More Velocity")) |
|||
self.actionVelocityMore.setShortcut(_translate("MainWindow", ">")) |
|||
self.actionVelocityLess.setText(_translate("MainWindow", "&Less Velocity")) |
|||
self.actionVelocityLess.setToolTip(_translate("MainWindow", "Less Velocity")) |
|||
self.actionVelocityLess.setShortcut(_translate("MainWindow", "<")) |
|||
self.actionDurationModMore.setText(_translate("MainWindow", "Lo&nger Duration Mod.")) |
|||
self.actionDurationModMore.setToolTip(_translate("MainWindow", "Longer Duration Mod.")) |
|||
self.actionDurationModMore.setShortcut(_translate("MainWindow", "Alt+Shift+Right")) |
|||
self.actionDurationModLess.setText(_translate("MainWindow", "Shorter Duration Mod.")) |
|||
self.actionDurationModLess.setToolTip(_translate("MainWindow", "Shorter Duration Mod.")) |
|||
self.actionDurationModLess.setShortcut(_translate("MainWindow", "Alt+Shift+Left")) |
|||
self.actionReset_Velocity_Duration_Mod.setText(_translate("MainWindow", "&Reset Velocity / Duration Mod.")) |
|||
self.actionWiden_Score_View.setText(_translate("MainWindow", "&Widen Score View")) |
|||
self.actionWiden_Score_View.setShortcut(_translate("MainWindow", "Ctrl++")) |
|||
self.actionShrink_Score_View.setText(_translate("MainWindow", "Shr&ink Score View")) |
|||
self.actionShrink_Score_View.setShortcut(_translate("MainWindow", "Ctrl+-")) |
|||
self.actionData_Editor.setText(_translate("MainWindow", "&Track Editor")) |
|||
self.actionData_Editor.setShortcut(_translate("MainWindow", "Ctrl+T")) |
|||
self.actionClef.setText(_translate("MainWindow", "&Clef")) |
|||
self.actionClef.setShortcut(_translate("MainWindow", "C")) |
|||
self.actionMetrical_Instruction.setText(_translate("MainWindow", "&Metrical Instruction")) |
|||
self.actionMetrical_Instruction.setShortcut(_translate("MainWindow", "M")) |
|||
self.actionKey_Signature.setText(_translate("MainWindow", "&Key Signature")) |
|||
self.actionKey_Signature.setShortcut(_translate("MainWindow", "K")) |
|||
self.actionDynamics.setText(_translate("MainWindow", "&Dynamics")) |
|||
self.actionDynamics.setShortcut(_translate("MainWindow", "D")) |
|||
self.actionMulti_Measure_Rest.setText(_translate("MainWindow", "Multi Measure &Rest")) |
|||
self.actionMulti_Measure_Rest.setShortcut(_translate("MainWindow", "R")) |
|||
self.actionTemporary_Tempo_Change.setText(_translate("MainWindow", "Tem&porary Tempo Change")) |
|||
self.actionTemporary_Tempo_Change.setShortcut(_translate("MainWindow", "F")) |
|||
self.actionBeam.setText(_translate("MainWindow", "&Beam")) |
|||
self.actionBeam.setShortcut(_translate("MainWindow", "B")) |
|||
self.actionRemoveBeams.setText(_translate("MainWindow", "&Remove Beams")) |
|||
self.actionRemoveBeams.setShortcut(_translate("MainWindow", "Alt+B")) |
|||
self.actionTempo_Change.setText(_translate("MainWindow", "&Tempo Change")) |
|||
self.actionTempo_Change.setShortcut(_translate("MainWindow", "G")) |
|||
self.actionDelete_Tempo_Change.setText(_translate("MainWindow", "D&elete Tempo Change")) |
|||
self.actionDelete_Tempo_Change.setShortcut(_translate("MainWindow", "Alt+G")) |
|||
self.actionBlock_Mode.setText(_translate("MainWindow", "&Blocks Only")) |
|||
self.actionBlock_Mode.setShortcut(_translate("MainWindow", "F6")) |
|||
self.actionNotation_Mode.setText(_translate("MainWindow", "&Notation")) |
|||
self.actionNotation_Mode.setShortcut(_translate("MainWindow", "F5")) |
|||
self.actionTransposeChord.setText(_translate("MainWindow", "&Transpose Chord")) |
|||
self.actionTranspose_Score.setText(_translate("MainWindow", "&Transpose Score")) |
|||
self.actionSave.setText(_translate("MainWindow", "&Save")) |
|||
self.actionSave.setShortcut(_translate("MainWindow", "Ctrl+S")) |
|||
self.actionExport_File.setText(_translate("MainWindow", "Export File")) |
|||
self.actionImport_File_replaces_current.setText(_translate("MainWindow", "Import File (replaces current)")) |
|||
self.actionUnlink_Current_Block.setText(_translate("MainWindow", "&Unlink Current Block")) |
|||
self.actionDelete_All_Empty_Blocks.setText(_translate("MainWindow", "Delete All &Empty Blocks")) |
|||
self.actionBlockRight.setText(_translate("MainWindow", "blo&ckRight")) |
|||
self.actionBlockRight.setShortcut(_translate("MainWindow", "Ctrl+Alt+Right")) |
|||
self.actionBlockLeft.setText(_translate("MainWindow", "&blockLeft")) |
|||
self.actionBlockLeft.setShortcut(_translate("MainWindow", "Ctrl+Alt+Left")) |
|||
self.actionSelectBlockRight.setText(_translate("MainWindow", "&selectBlockRight")) |
|||
self.actionSelectBlockRight.setShortcut(_translate("MainWindow", "Ctrl+Alt+Shift+Right")) |
|||
self.actionSelectBlockLeft.setText(_translate("MainWindow", "s&electBlockLeft")) |
|||
self.actionSelectBlockLeft.setShortcut(_translate("MainWindow", "Ctrl+Alt+Shift+Left")) |
|||
self.actionPedalNotes.setText(_translate("MainWindow", "&Pedal Notes")) |
|||
self.actionReverse.setText(_translate("MainWindow", "&Reverse")) |
|||
self.actionDuplicateItem.setText(_translate("MainWindow", "Dup&licate")) |
|||
self.actionDuplicateItem.setShortcut(_translate("MainWindow", "Ctrl+D")) |
|||
self.actionRandom.setText(_translate("MainWindow", "&Shuffle")) |
|||
self.actionAscending.setText(_translate("MainWindow", "&Ascending")) |
|||
self.actionDescending.setText(_translate("MainWindow", "&Descending")) |
|||
self.actionMirror_around_Cursor.setText(_translate("MainWindow", "&Mirror around Cursor")) |
|||
self.actionInsert_Random.setText(_translate("MainWindow", "Insert Random")) |
|||
self.actionRandom_chromatic_in_clef_range.setText(_translate("MainWindow", "&Random chromatic in clef range")) |
|||
self.actionRandom_in_scale_in_clef_range.setText(_translate("MainWindow", "Random &in-scale in clef range")) |
|||
self.actionRandom_pitch_from_clipboard.setText(_translate("MainWindow", "Random pitch &from clipboard")) |
|||
self.actionProperties.setText(_translate("MainWindow", "Meta&data")) |
|||
self.actionExport_to_Ly.setText(_translate("MainWindow", "&Export to .ly file")) |
|||
self.actionShow_PDF.setText(_translate("MainWindow", "&Show PDF")) |
|||
self.actionShow_PDF.setShortcut(_translate("MainWindow", "Ctrl+P")) |
|||
self.actionExport_to_pdf_file.setText(_translate("MainWindow", "Export to .pdf file")) |
|||
self.actionInstrument_Change.setText(_translate("MainWindow", "&Instrument Change")) |
|||
self.actionProgram_Change_Relative.setText(_translate("MainWindow", "Program Change Relative")) |
|||
self.actionBank_Change_Absolute.setText(_translate("MainWindow", "Insert Bank Change")) |
|||
self.actionBank_Change_Relative.setText(_translate("MainWindow", "Bank Change Relative")) |
|||
self.actionChannel_Change.setText(_translate("MainWindow", "&Insert Channel Change ")) |
|||
self.actionChannel_Change_Relative.setText(_translate("MainWindow", "Channel Change Relative")) |
|||
self.actionMIDI_Channel_Plus.setText(_translate("MainWindow", "&MIDI Channel +")) |
|||
self.actionMIDI_Channel_Minus.setText(_translate("MainWindow", "MIDI &Channel -")) |
|||
self.actionMIDI_Channel_Reset.setText(_translate("MainWindow", "MIDI C&hannel Reset")) |
|||
self.actionDelete_Current_Track.setText(_translate("MainWindow", "Delete Current Track")) |
|||
self.actionUse_Current_Track_as_Metronome.setText(_translate("MainWindow", "Use Current Track as &Metronome")) |
|||
self.actionUse_Current_Track_as_Metronome.setShortcut(_translate("MainWindow", "U")) |
|||
self.actionMetronome_Enabled.setText(_translate("MainWindow", "Metronome &Enabled")) |
|||
self.actionMetronome_Enabled.setShortcut(_translate("MainWindow", "Alt+U")) |
|||
self.actionRandom_in_scale_in_cursor_plus_octave.setText(_translate("MainWindow", "Random in-scale in cursor plus octave (authentic mode)")) |
|||
self.actionRandom_in_scale_in_octave_around_cursor.setText(_translate("MainWindow", "Random in-scale in octave around cursor (hypo mode)")) |
|||
self.actionCreate_pool_from_selection.setText(_translate("MainWindow", "Create pool from selection")) |
|||
|
@ -0,0 +1,99 @@ |
|||
# -*- coding: utf-8 -*- |
|||
|
|||
# Form implementation generated from reading ui file 'tickWidget.ui' |
|||
# |
|||
# Created by: PyQt5 UI code generator 5.7 |
|||
# |
|||
# WARNING! All changes made in this file will be lost! |
|||
|
|||
from PyQt5 import QtCore, QtGui, QtWidgets |
|||
|
|||
class Ui_tickWidget(object): |
|||
def setupUi(self, tickWidget): |
|||
tickWidget.setObjectName("tickWidget") |
|||
tickWidget.resize(453, 229) |
|||
self.D1 = QtWidgets.QPushButton(tickWidget) |
|||
self.D1.setGeometry(QtCore.QRect(10, 40, 80, 22)) |
|||
self.D1.setObjectName("D1") |
|||
self.D2 = QtWidgets.QPushButton(tickWidget) |
|||
self.D2.setGeometry(QtCore.QRect(100, 40, 80, 22)) |
|||
self.D2.setObjectName("D2") |
|||
self.D4 = QtWidgets.QPushButton(tickWidget) |
|||
self.D4.setGeometry(QtCore.QRect(190, 40, 80, 22)) |
|||
self.D4.setObjectName("D4") |
|||
self.D8 = QtWidgets.QPushButton(tickWidget) |
|||
self.D8.setGeometry(QtCore.QRect(280, 40, 80, 22)) |
|||
self.D8.setObjectName("D8") |
|||
self.D16 = QtWidgets.QPushButton(tickWidget) |
|||
self.D16.setGeometry(QtCore.QRect(370, 40, 80, 22)) |
|||
self.D16.setObjectName("D16") |
|||
self.DB = QtWidgets.QPushButton(tickWidget) |
|||
self.DB.setGeometry(QtCore.QRect(10, 80, 80, 22)) |
|||
self.DB.setObjectName("DB") |
|||
self.DL = QtWidgets.QPushButton(tickWidget) |
|||
self.DL.setGeometry(QtCore.QRect(100, 80, 80, 22)) |
|||
self.DL.setObjectName("DL") |
|||
self.D32 = QtWidgets.QPushButton(tickWidget) |
|||
self.D32.setGeometry(QtCore.QRect(190, 80, 80, 22)) |
|||
self.D32.setObjectName("D32") |
|||
self.D64 = QtWidgets.QPushButton(tickWidget) |
|||
self.D64.setGeometry(QtCore.QRect(280, 80, 80, 22)) |
|||
self.D64.setObjectName("D64") |
|||
self.D128 = QtWidgets.QPushButton(tickWidget) |
|||
self.D128.setGeometry(QtCore.QRect(370, 80, 80, 22)) |
|||
self.D128.setObjectName("D128") |
|||
self.ticks = QtWidgets.QSpinBox(tickWidget) |
|||
self.ticks.setGeometry(QtCore.QRect(160, 190, 161, 23)) |
|||
self.ticks.setReadOnly(True) |
|||
self.ticks.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons) |
|||
self.ticks.setPrefix("") |
|||
self.ticks.setMinimum(0) |
|||
self.ticks.setMaximum(999999999) |
|||
self.ticks.setObjectName("ticks") |
|||
self.ok = QtWidgets.QPushButton(tickWidget) |
|||
self.ok.setGeometry(QtCore.QRect(60, 190, 80, 22)) |
|||
self.ok.setObjectName("ok") |
|||
self.cancel = QtWidgets.QPushButton(tickWidget) |
|||
self.cancel.setGeometry(QtCore.QRect(340, 190, 80, 22)) |
|||
self.cancel.setObjectName("cancel") |
|||
self.durationLabel = QtWidgets.QLabel(tickWidget) |
|||
self.durationLabel.setGeometry(QtCore.QRect(30, 150, 401, 31)) |
|||
self.durationLabel.setAlignment(QtCore.Qt.AlignCenter) |
|||
self.durationLabel.setObjectName("durationLabel") |
|||
self.reset = QtWidgets.QPushButton(tickWidget) |
|||
self.reset.setGeometry(QtCore.QRect(190, 120, 80, 22)) |
|||
self.reset.setObjectName("reset") |
|||
self.label = QtWidgets.QLabel(tickWidget) |
|||
self.label.setGeometry(QtCore.QRect(120, 10, 241, 20)) |
|||
self.label.setObjectName("label") |
|||
|
|||
self.retranslateUi(tickWidget) |
|||
QtCore.QMetaObject.connectSlotsByName(tickWidget) |
|||
|
|||
def retranslateUi(self, tickWidget): |
|||
_translate = QtCore.QCoreApplication.translate |
|||
tickWidget.setWindowTitle(_translate("tickWidget", "Tick Calculator")) |
|||
self.D1.setText(_translate("tickWidget", "[1] D1")) |
|||
self.D1.setShortcut(_translate("tickWidget", "1")) |
|||
self.D2.setText(_translate("tickWidget", "[2] D2")) |
|||
self.D2.setShortcut(_translate("tickWidget", "2")) |
|||
self.D4.setText(_translate("tickWidget", "[3] D4")) |
|||
self.D4.setShortcut(_translate("tickWidget", "3")) |
|||
self.D8.setText(_translate("tickWidget", "[4] D8")) |
|||
self.D8.setShortcut(_translate("tickWidget", "4")) |
|||
self.D16.setText(_translate("tickWidget", "[5] D16")) |
|||
self.D16.setShortcut(_translate("tickWidget", "5")) |
|||
self.DB.setText(_translate("tickWidget", "DBrevis")) |
|||
self.DL.setText(_translate("tickWidget", "DLonga")) |
|||
self.D32.setText(_translate("tickWidget", "D32")) |
|||
self.D64.setText(_translate("tickWidget", "D64")) |
|||
self.D128.setText(_translate("tickWidget", "D128")) |
|||
self.ticks.setSuffix(_translate("tickWidget", " Ticks")) |
|||
self.ok.setText(_translate("tickWidget", "OK")) |
|||
self.ok.setShortcut(_translate("tickWidget", "Return")) |
|||
self.cancel.setText(_translate("tickWidget", "Cancel")) |
|||
self.cancel.setShortcut(_translate("tickWidget", "Esc")) |
|||
self.durationLabel.setText(_translate("tickWidget", "Simplest Duration")) |
|||
self.reset.setText(_translate("tickWidget", "Reset")) |
|||
self.label.setText(_translate("tickWidget", "Use buttons to add to total tick duration")) |
|||
|
@ -0,0 +1,266 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<ui version="4.0"> |
|||
<class>tickWidget</class> |
|||
<widget class="QWidget" name="tickWidget"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>0</x> |
|||
<y>0</y> |
|||
<width>453</width> |
|||
<height>229</height> |
|||
</rect> |
|||
</property> |
|||
<property name="windowTitle"> |
|||
<string>Tick Calculator</string> |
|||
</property> |
|||
<widget class="QPushButton" name="D1"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>10</x> |
|||
<y>40</y> |
|||
<width>80</width> |
|||
<height>22</height> |
|||
</rect> |
|||
</property> |
|||
<property name="text"> |
|||
<string>[1] D1</string> |
|||
</property> |
|||
<property name="shortcut"> |
|||
<string>1</string> |
|||
</property> |
|||
</widget> |
|||
<widget class="QPushButton" name="D2"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>100</x> |
|||
<y>40</y> |
|||
<width>80</width> |
|||
<height>22</height> |
|||
</rect> |
|||
</property> |
|||
<property name="text"> |
|||
<string>[2] D2</string> |
|||
</property> |
|||
<property name="shortcut"> |
|||
<string>2</string> |
|||
</property> |
|||
</widget> |
|||
<widget class="QPushButton" name="D4"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>190</x> |
|||
<y>40</y> |
|||
<width>80</width> |
|||
<height>22</height> |
|||
</rect> |
|||
</property> |
|||
<property name="text"> |
|||
<string>[3] D4</string> |
|||
</property> |
|||
<property name="shortcut"> |
|||
<string>3</string> |
|||
</property> |
|||
</widget> |
|||
<widget class="QPushButton" name="D8"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>280</x> |
|||
<y>40</y> |
|||
<width>80</width> |
|||
<height>22</height> |
|||
</rect> |
|||
</property> |
|||
<property name="text"> |
|||
<string>[4] D8</string> |
|||
</property> |
|||
<property name="shortcut"> |
|||
<string>4</string> |
|||
</property> |
|||
</widget> |
|||
<widget class="QPushButton" name="D16"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>370</x> |
|||
<y>40</y> |
|||
<width>80</width> |
|||
<height>22</height> |
|||
</rect> |
|||
</property> |
|||
<property name="text"> |
|||
<string>[5] D16</string> |
|||
</property> |
|||
<property name="shortcut"> |
|||
<string>5</string> |
|||
</property> |
|||
</widget> |
|||
<widget class="QPushButton" name="DB"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>10</x> |
|||
<y>80</y> |
|||
<width>80</width> |
|||
<height>22</height> |
|||
</rect> |
|||
</property> |
|||
<property name="text"> |
|||
<string>DBrevis</string> |
|||
</property> |
|||
</widget> |
|||
<widget class="QPushButton" name="DL"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>100</x> |
|||
<y>80</y> |
|||
<width>80</width> |
|||
<height>22</height> |
|||
</rect> |
|||
</property> |
|||
<property name="text"> |
|||
<string>DLonga</string> |
|||
</property> |
|||
</widget> |
|||
<widget class="QPushButton" name="D32"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>190</x> |
|||
<y>80</y> |
|||
<width>80</width> |
|||
<height>22</height> |
|||
</rect> |
|||
</property> |
|||
<property name="text"> |
|||
<string>D32</string> |
|||
</property> |
|||
</widget> |
|||
<widget class="QPushButton" name="D64"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>280</x> |
|||
<y>80</y> |
|||
<width>80</width> |
|||
<height>22</height> |
|||
</rect> |
|||
</property> |
|||
<property name="text"> |
|||
<string>D64</string> |
|||
</property> |
|||
</widget> |
|||
<widget class="QPushButton" name="D128"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>370</x> |
|||
<y>80</y> |
|||
<width>80</width> |
|||
<height>22</height> |
|||
</rect> |
|||
</property> |
|||
<property name="text"> |
|||
<string>D128</string> |
|||
</property> |
|||
</widget> |
|||
<widget class="QSpinBox" name="ticks"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>160</x> |
|||
<y>190</y> |
|||
<width>161</width> |
|||
<height>23</height> |
|||
</rect> |
|||
</property> |
|||
<property name="readOnly"> |
|||
<bool>true</bool> |
|||
</property> |
|||
<property name="buttonSymbols"> |
|||
<enum>QAbstractSpinBox::NoButtons</enum> |
|||
</property> |
|||
<property name="suffix"> |
|||
<string> Ticks</string> |
|||
</property> |
|||
<property name="prefix"> |
|||
<string/> |
|||
</property> |
|||
<property name="minimum"> |
|||
<number>0</number> |
|||
</property> |
|||
<property name="maximum"> |
|||
<number>999999999</number> |
|||
</property> |
|||
</widget> |
|||
<widget class="QPushButton" name="ok"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>60</x> |
|||
<y>190</y> |
|||
<width>80</width> |
|||
<height>22</height> |
|||
</rect> |
|||
</property> |
|||
<property name="text"> |
|||
<string>OK</string> |
|||
</property> |
|||
<property name="shortcut"> |
|||
<string>Return</string> |
|||
</property> |
|||
</widget> |
|||
<widget class="QPushButton" name="cancel"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>340</x> |
|||
<y>190</y> |
|||
<width>80</width> |
|||
<height>22</height> |
|||
</rect> |
|||
</property> |
|||
<property name="text"> |
|||
<string>Cancel</string> |
|||
</property> |
|||
<property name="shortcut"> |
|||
<string>Esc</string> |
|||
</property> |
|||
</widget> |
|||
<widget class="QLabel" name="durationLabel"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>30</x> |
|||
<y>150</y> |
|||
<width>401</width> |
|||
<height>31</height> |
|||
</rect> |
|||
</property> |
|||
<property name="text"> |
|||
<string>Simplest Duration</string> |
|||
</property> |
|||
<property name="alignment"> |
|||
<set>Qt::AlignCenter</set> |
|||
</property> |
|||
</widget> |
|||
<widget class="QPushButton" name="reset"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>190</x> |
|||
<y>120</y> |
|||
<width>80</width> |
|||
<height>22</height> |
|||
</rect> |
|||
</property> |
|||
<property name="text"> |
|||
<string>Reset</string> |
|||
</property> |
|||
</widget> |
|||
<widget class="QLabel" name="label"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>120</x> |
|||
<y>10</y> |
|||
<width>241</width> |
|||
<height>20</height> |
|||
</rect> |
|||
</property> |
|||
<property name="text"> |
|||
<string>Use buttons to add to total tick duration</string> |
|||
</property> |
|||
</widget> |
|||
</widget> |
|||
<resources/> |
|||
<connections/> |
|||
</ui> |
@ -0,0 +1,491 @@ |
|||
# -*- coding: utf-8 -*- |
|||
|
|||
# Form implementation generated from reading ui file 'trackWidget.ui' |
|||
# |
|||
# Created by: PyQt5 UI code generator 5.9.2 |
|||
# |
|||
# WARNING! All changes made in this file will be lost! |
|||
|
|||
from PyQt5 import QtCore, QtGui, QtWidgets |
|||
|
|||
class Ui_trackGroupWidget(object): |
|||
def setupUi(self, trackGroupWidget): |
|||
trackGroupWidget.setObjectName("trackGroupWidget") |
|||
trackGroupWidget.resize(1374, 530) |
|||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) |
|||
sizePolicy.setHorizontalStretch(0) |
|||
sizePolicy.setVerticalStretch(0) |
|||
sizePolicy.setHeightForWidth(trackGroupWidget.sizePolicy().hasHeightForWidth()) |
|||
trackGroupWidget.setSizePolicy(sizePolicy) |
|||
trackGroupWidget.setMaximumSize(QtCore.QSize(16777215, 900)) |
|||
self.verticalLayout = QtWidgets.QVBoxLayout(trackGroupWidget) |
|||
self.verticalLayout.setContentsMargins(3, 3, 3, 3) |
|||
self.verticalLayout.setSpacing(3) |
|||
self.verticalLayout.setObjectName("verticalLayout") |
|||
self.track = QtWidgets.QWidget(trackGroupWidget) |
|||
self.track.setMaximumSize(QtCore.QSize(16777215, 45)) |
|||
self.track.setObjectName("track") |
|||
self.horizontalLayout = QtWidgets.QHBoxLayout(self.track) |
|||
self.horizontalLayout.setContentsMargins(3, 3, 3, 3) |
|||
self.horizontalLayout.setSpacing(3) |
|||
self.horizontalLayout.setObjectName("horizontalLayout") |
|||
self.nameLineEdit = QtWidgets.QLineEdit(self.track) |
|||
self.nameLineEdit.setEnabled(True) |
|||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed) |
|||
sizePolicy.setHorizontalStretch(0) |
|||
sizePolicy.setVerticalStretch(0) |
|||
sizePolicy.setHeightForWidth(self.nameLineEdit.sizePolicy().hasHeightForWidth()) |
|||
self.nameLineEdit.setSizePolicy(sizePolicy) |
|||
self.nameLineEdit.setMinimumSize(QtCore.QSize(200, 0)) |
|||
self.nameLineEdit.setObjectName("nameLineEdit") |
|||
self.horizontalLayout.addWidget(self.nameLineEdit) |
|||
self.frame = QtWidgets.QFrame(self.track) |
|||
self.frame.setFrameShape(QtWidgets.QFrame.Box) |
|||
self.frame.setFrameShadow(QtWidgets.QFrame.Sunken) |
|||
self.frame.setObjectName("frame") |
|||
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.frame) |
|||
self.horizontalLayout_3.setContentsMargins(3, 0, 3, 0) |
|||
self.horizontalLayout_3.setSpacing(0) |
|||
self.horizontalLayout_3.setObjectName("horizontalLayout_3") |
|||
self.upbeatSpinBox = QtWidgets.QSpinBox(self.frame) |
|||
self.upbeatSpinBox.setPrefix("") |
|||
self.upbeatSpinBox.setMaximum(999999999) |
|||
self.upbeatSpinBox.setObjectName("upbeatSpinBox") |
|||
self.horizontalLayout_3.addWidget(self.upbeatSpinBox) |
|||
self.callTickWidget = QtWidgets.QPushButton(self.frame) |
|||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) |
|||
sizePolicy.setHorizontalStretch(0) |
|||
sizePolicy.setVerticalStretch(0) |
|||
sizePolicy.setHeightForWidth(self.callTickWidget.sizePolicy().hasHeightForWidth()) |
|||
self.callTickWidget.setSizePolicy(sizePolicy) |
|||
self.callTickWidget.setMaximumSize(QtCore.QSize(25, 16777215)) |
|||
self.callTickWidget.setFlat(False) |
|||
self.callTickWidget.setObjectName("callTickWidget") |
|||
self.horizontalLayout_3.addWidget(self.callTickWidget) |
|||
self.horizontalLayout.addWidget(self.frame) |
|||
self.doubleTrackCheckbox = QtWidgets.QCheckBox(self.track) |
|||
self.doubleTrackCheckbox.setObjectName("doubleTrackCheckbox") |
|||
self.horizontalLayout.addWidget(self.doubleTrackCheckbox) |
|||
self.visibleCheckbox = QtWidgets.QCheckBox(self.track) |
|||
self.visibleCheckbox.setChecked(True) |
|||
self.visibleCheckbox.setObjectName("visibleCheckbox") |
|||
self.horizontalLayout.addWidget(self.visibleCheckbox) |
|||
self.frame_2 = QtWidgets.QFrame(self.track) |
|||
self.frame_2.setFrameShape(QtWidgets.QFrame.Box) |
|||
self.frame_2.setFrameShadow(QtWidgets.QFrame.Sunken) |
|||
self.frame_2.setObjectName("frame_2") |
|||
self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.frame_2) |
|||
self.horizontalLayout_4.setContentsMargins(3, 0, 3, 0) |
|||
self.horizontalLayout_4.setSpacing(3) |
|||
self.horizontalLayout_4.setObjectName("horizontalLayout_4") |
|||
self.midiChannelSpinBox = QtWidgets.QSpinBox(self.frame_2) |
|||
self.midiChannelSpinBox.setMaximum(15) |
|||
self.midiChannelSpinBox.setObjectName("midiChannelSpinBox") |
|||
self.horizontalLayout_4.addWidget(self.midiChannelSpinBox) |
|||
self.ccChannelsPushButton = QtWidgets.QPushButton(self.frame_2) |
|||
self.ccChannelsPushButton.setObjectName("ccChannelsPushButton") |
|||
self.horizontalLayout_4.addWidget(self.ccChannelsPushButton) |
|||
self.midiProgramSpinBox = QtWidgets.QSpinBox(self.frame_2) |
|||
self.midiProgramSpinBox.setMinimum(-1) |
|||
self.midiProgramSpinBox.setMaximum(127) |
|||
self.midiProgramSpinBox.setProperty("value", -1) |
|||
self.midiProgramSpinBox.setObjectName("midiProgramSpinBox") |
|||
self.horizontalLayout_4.addWidget(self.midiProgramSpinBox) |
|||
self.midiBankMsbSpinBox = QtWidgets.QSpinBox(self.frame_2) |
|||
self.midiBankMsbSpinBox.setEnabled(False) |
|||
self.midiBankMsbSpinBox.setMinimum(0) |
|||
self.midiBankMsbSpinBox.setMaximum(127) |
|||
self.midiBankMsbSpinBox.setProperty("value", 0) |
|||
self.midiBankMsbSpinBox.setObjectName("midiBankMsbSpinBox") |
|||
self.horizontalLayout_4.addWidget(self.midiBankMsbSpinBox) |
|||
self.midiTransposeSpinBox = QtWidgets.QSpinBox(self.frame_2) |
|||
self.midiTransposeSpinBox.setMinimum(-127) |
|||
self.midiTransposeSpinBox.setMaximum(128) |
|||
self.midiTransposeSpinBox.setObjectName("midiTransposeSpinBox") |
|||
self.horizontalLayout_4.addWidget(self.midiTransposeSpinBox) |
|||
self.midiBankLsbSpinBox = QtWidgets.QSpinBox(self.frame_2) |
|||
self.midiBankLsbSpinBox.setEnabled(False) |
|||
self.midiBankLsbSpinBox.setMinimum(0) |
|||
self.midiBankLsbSpinBox.setMaximum(127) |
|||
self.midiBankLsbSpinBox.setProperty("value", 0) |
|||
self.midiBankLsbSpinBox.setObjectName("midiBankLsbSpinBox") |
|||
self.horizontalLayout_4.addWidget(self.midiBankLsbSpinBox) |
|||
self.horizontalLayout.addWidget(self.frame_2) |
|||
self.deleteButton = QtWidgets.QPushButton(self.track) |
|||
self.deleteButton.setObjectName("deleteButton") |
|||
self.horizontalLayout.addWidget(self.deleteButton) |
|||
spacerItem = QtWidgets.QSpacerItem(640, 477, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) |
|||
self.horizontalLayout.addItem(spacerItem) |
|||
self.verticalLayout.addWidget(self.track) |
|||
self.horizontalLayout_2 = QtWidgets.QHBoxLayout() |
|||
self.horizontalLayout_2.setObjectName("horizontalLayout_2") |
|||
self.label = QtWidgets.QLabel(trackGroupWidget) |
|||
self.label.setObjectName("label") |
|||
self.horizontalLayout_2.addWidget(self.label) |
|||
self.instrumentName = QtWidgets.QLineEdit(trackGroupWidget) |
|||
self.instrumentName.setMaxLength(32767) |
|||
self.instrumentName.setObjectName("instrumentName") |
|||
self.horizontalLayout_2.addWidget(self.instrumentName) |
|||
self.label_2 = QtWidgets.QLabel(trackGroupWidget) |
|||
self.label_2.setObjectName("label_2") |
|||
self.horizontalLayout_2.addWidget(self.label_2) |
|||
self.shortInstrumentName = QtWidgets.QLineEdit(trackGroupWidget) |
|||
self.shortInstrumentName.setMaxLength(5) |
|||
self.shortInstrumentName.setObjectName("shortInstrumentName") |
|||
self.horizontalLayout_2.addWidget(self.shortInstrumentName) |
|||
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) |
|||
self.horizontalLayout_2.addItem(spacerItem1) |
|||
self.verticalLayout.addLayout(self.horizontalLayout_2) |
|||
self.advanced = QtWidgets.QGroupBox(trackGroupWidget) |
|||
self.advanced.setCheckable(True) |
|||
self.advanced.setObjectName("advanced") |
|||
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.advanced) |
|||
self.verticalLayout_2.setObjectName("verticalLayout_2") |
|||
self.advancedContent = QtWidgets.QTabWidget(self.advanced) |
|||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum) |
|||
sizePolicy.setHorizontalStretch(0) |
|||
sizePolicy.setVerticalStretch(0) |
|||
sizePolicy.setHeightForWidth(self.advancedContent.sizePolicy().hasHeightForWidth()) |
|||
self.advancedContent.setSizePolicy(sizePolicy) |
|||
self.advancedContent.setObjectName("advancedContent") |
|||
self.tab = QtWidgets.QWidget() |
|||
self.tab.setObjectName("tab") |
|||
self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.tab) |
|||
self.verticalLayout_4.setObjectName("verticalLayout_4") |
|||
self.widget_3 = QtWidgets.QWidget(self.tab) |
|||
self.widget_3.setObjectName("widget_3") |
|||
self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.widget_3) |
|||
self.horizontalLayout_5.setObjectName("horizontalLayout_5") |
|||
self.widget_2 = QtWidgets.QWidget(self.widget_3) |
|||
self.widget_2.setObjectName("widget_2") |
|||
self.formLayout_4 = QtWidgets.QFormLayout(self.widget_2) |
|||
self.formLayout_4.setObjectName("formLayout_4") |
|||
self.label_3 = QtWidgets.QLabel(self.widget_2) |
|||
self.label_3.setObjectName("label_3") |
|||
self.formLayout_4.setWidget(7, QtWidgets.QFormLayout.LabelRole, self.label_3) |
|||
self.defaultOn = QtWidgets.QLineEdit(self.widget_2) |
|||
self.defaultOn.setObjectName("defaultOn") |
|||
self.formLayout_4.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.defaultOn) |
|||
self.label_4 = QtWidgets.QLabel(self.widget_2) |
|||
self.label_4.setObjectName("label_4") |
|||
self.formLayout_4.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.label_4) |
|||
self.defaultOff = QtWidgets.QLineEdit(self.widget_2) |
|||
self.defaultOff.setObjectName("defaultOff") |
|||
self.formLayout_4.setWidget(8, QtWidgets.QFormLayout.FieldRole, self.defaultOff) |
|||
self.label_6 = QtWidgets.QLabel(self.widget_2) |
|||
self.label_6.setObjectName("label_6") |
|||
self.formLayout_4.setWidget(9, QtWidgets.QFormLayout.LabelRole, self.label_6) |
|||
self.staccatoOn = QtWidgets.QLineEdit(self.widget_2) |
|||
self.staccatoOn.setObjectName("staccatoOn") |
|||
self.formLayout_4.setWidget(9, QtWidgets.QFormLayout.FieldRole, self.staccatoOn) |
|||
self.label_5 = QtWidgets.QLabel(self.widget_2) |
|||
self.label_5.setObjectName("label_5") |
|||
self.formLayout_4.setWidget(10, QtWidgets.QFormLayout.LabelRole, self.label_5) |
|||
self.staccatoOff = QtWidgets.QLineEdit(self.widget_2) |
|||
self.staccatoOff.setObjectName("staccatoOff") |
|||
self.formLayout_4.setWidget(10, QtWidgets.QFormLayout.FieldRole, self.staccatoOff) |
|||
self.label_8 = QtWidgets.QLabel(self.widget_2) |
|||
self.label_8.setObjectName("label_8") |
|||
self.formLayout_4.setWidget(11, QtWidgets.QFormLayout.LabelRole, self.label_8) |
|||
self.tenutoOn = QtWidgets.QLineEdit(self.widget_2) |
|||
self.tenutoOn.setObjectName("tenutoOn") |
|||
self.formLayout_4.setWidget(11, QtWidgets.QFormLayout.FieldRole, self.tenutoOn) |
|||
self.label_9 = QtWidgets.QLabel(self.widget_2) |
|||
self.label_9.setObjectName("label_9") |
|||
self.formLayout_4.setWidget(12, QtWidgets.QFormLayout.LabelRole, self.label_9) |
|||
self.tenutoOff = QtWidgets.QLineEdit(self.widget_2) |
|||
self.tenutoOff.setObjectName("tenutoOff") |
|||
self.formLayout_4.setWidget(12, QtWidgets.QFormLayout.FieldRole, self.tenutoOff) |
|||
self.label_10 = QtWidgets.QLabel(self.widget_2) |
|||
self.label_10.setObjectName("label_10") |
|||
self.formLayout_4.setWidget(13, QtWidgets.QFormLayout.LabelRole, self.label_10) |
|||
self.legatoOn = QtWidgets.QLineEdit(self.widget_2) |
|||
self.legatoOn.setObjectName("legatoOn") |
|||
self.formLayout_4.setWidget(13, QtWidgets.QFormLayout.FieldRole, self.legatoOn) |
|||
self.label_7 = QtWidgets.QLabel(self.widget_2) |
|||
self.label_7.setObjectName("label_7") |
|||
self.formLayout_4.setWidget(14, QtWidgets.QFormLayout.LabelRole, self.label_7) |
|||
self.legatoOff = QtWidgets.QLineEdit(self.widget_2) |
|||
self.legatoOff.setObjectName("legatoOff") |
|||
self.formLayout_4.setWidget(14, QtWidgets.QFormLayout.FieldRole, self.legatoOff) |
|||
self.horizontalLayout_5.addWidget(self.widget_2) |
|||
spacerItem2 = QtWidgets.QSpacerItem(643, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) |
|||
self.horizontalLayout_5.addItem(spacerItem2) |
|||
self.verticalLayout_4.addWidget(self.widget_3) |
|||
self.buttonResetDurations = QtWidgets.QPushButton(self.tab) |
|||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) |
|||
sizePolicy.setHorizontalStretch(0) |
|||
sizePolicy.setVerticalStretch(0) |
|||
sizePolicy.setHeightForWidth(self.buttonResetDurations.sizePolicy().hasHeightForWidth()) |
|||
self.buttonResetDurations.setSizePolicy(sizePolicy) |
|||
self.buttonResetDurations.setObjectName("buttonResetDurations") |
|||
self.verticalLayout_4.addWidget(self.buttonResetDurations) |
|||
self.advancedContent.addTab(self.tab, "") |
|||
self.tab_2 = QtWidgets.QWidget() |
|||
self.tab_2.setObjectName("tab_2") |
|||
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.tab_2) |
|||
self.verticalLayout_3.setObjectName("verticalLayout_3") |
|||
self.widget = QtWidgets.QWidget(self.tab_2) |
|||
self.widget.setObjectName("widget") |
|||
self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.widget) |
|||
self.horizontalLayout_6.setObjectName("horizontalLayout_6") |
|||
self.widget_4 = QtWidgets.QWidget(self.widget) |
|||
self.widget_4.setObjectName("widget_4") |
|||
self.formLayout = QtWidgets.QFormLayout(self.widget_4) |
|||
self.formLayout.setObjectName("formLayout") |
|||
self.label_11 = QtWidgets.QLabel(self.widget_4) |
|||
self.label_11.setObjectName("label_11") |
|||
self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_11) |
|||
self.dynamics_ppppp = QtWidgets.QSpinBox(self.widget_4) |
|||
self.dynamics_ppppp.setMaximum(127) |
|||
self.dynamics_ppppp.setObjectName("dynamics_ppppp") |
|||
self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.dynamics_ppppp) |
|||
self.label_13 = QtWidgets.QLabel(self.widget_4) |
|||
self.label_13.setObjectName("label_13") |
|||
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_13) |
|||
self.dynamics_pppp = QtWidgets.QSpinBox(self.widget_4) |
|||
self.dynamics_pppp.setMaximum(127) |
|||
self.dynamics_pppp.setObjectName("dynamics_pppp") |
|||
self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.dynamics_pppp) |
|||
self.label_29 = QtWidgets.QLabel(self.widget_4) |
|||
self.label_29.setObjectName("label_29") |
|||
self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_29) |
|||
self.dynamics_ppp = QtWidgets.QSpinBox(self.widget_4) |
|||
self.dynamics_ppp.setObjectName("dynamics_ppp") |
|||
self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.dynamics_ppp) |
|||
self.label_14 = QtWidgets.QLabel(self.widget_4) |
|||
self.label_14.setObjectName("label_14") |
|||
self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_14) |
|||
self.dynamics_pp = QtWidgets.QSpinBox(self.widget_4) |
|||
self.dynamics_pp.setMaximum(127) |
|||
self.dynamics_pp.setObjectName("dynamics_pp") |
|||
self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.dynamics_pp) |
|||
self.label_15 = QtWidgets.QLabel(self.widget_4) |
|||
self.label_15.setObjectName("label_15") |
|||
self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_15) |
|||
self.dynamics_p = QtWidgets.QSpinBox(self.widget_4) |
|||
self.dynamics_p.setMaximum(127) |
|||
self.dynamics_p.setObjectName("dynamics_p") |
|||
self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.dynamics_p) |
|||
self.label_16 = QtWidgets.QLabel(self.widget_4) |
|||
self.label_16.setObjectName("label_16") |
|||
self.formLayout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.label_16) |
|||
self.dynamics_mp = QtWidgets.QSpinBox(self.widget_4) |
|||
self.dynamics_mp.setMaximum(127) |
|||
self.dynamics_mp.setObjectName("dynamics_mp") |
|||
self.formLayout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.dynamics_mp) |
|||
self.horizontalLayout_6.addWidget(self.widget_4) |
|||
self.widgetLeft = QtWidgets.QWidget(self.widget) |
|||
self.widgetLeft.setObjectName("widgetLeft") |
|||
self.formLayout_2 = QtWidgets.QFormLayout(self.widgetLeft) |
|||
self.formLayout_2.setObjectName("formLayout_2") |
|||
self.label_17 = QtWidgets.QLabel(self.widgetLeft) |
|||
self.label_17.setObjectName("label_17") |
|||
self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_17) |
|||
self.dynamics_mf = QtWidgets.QSpinBox(self.widgetLeft) |
|||
self.dynamics_mf.setMaximum(127) |
|||
self.dynamics_mf.setObjectName("dynamics_mf") |
|||
self.formLayout_2.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.dynamics_mf) |
|||
self.label_18 = QtWidgets.QLabel(self.widgetLeft) |
|||
self.label_18.setObjectName("label_18") |
|||
self.formLayout_2.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.label_18) |
|||
self.dynamics_f = QtWidgets.QSpinBox(self.widgetLeft) |
|||
self.dynamics_f.setMaximum(127) |
|||
self.dynamics_f.setObjectName("dynamics_f") |
|||
self.formLayout_2.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.dynamics_f) |
|||
self.label_19 = QtWidgets.QLabel(self.widgetLeft) |
|||
self.label_19.setObjectName("label_19") |
|||
self.formLayout_2.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.label_19) |
|||
self.dynamics_ff = QtWidgets.QSpinBox(self.widgetLeft) |
|||
self.dynamics_ff.setMaximum(127) |
|||
self.dynamics_ff.setObjectName("dynamics_ff") |
|||
self.formLayout_2.setWidget(8, QtWidgets.QFormLayout.FieldRole, self.dynamics_ff) |
|||
self.label_20 = QtWidgets.QLabel(self.widgetLeft) |
|||
self.label_20.setObjectName("label_20") |
|||
self.formLayout_2.setWidget(9, QtWidgets.QFormLayout.LabelRole, self.label_20) |
|||
self.dynamics_fff = QtWidgets.QSpinBox(self.widgetLeft) |
|||
self.dynamics_fff.setMaximum(127) |
|||
self.dynamics_fff.setObjectName("dynamics_fff") |
|||
self.formLayout_2.setWidget(9, QtWidgets.QFormLayout.FieldRole, self.dynamics_fff) |
|||
self.label_21 = QtWidgets.QLabel(self.widgetLeft) |
|||
self.label_21.setObjectName("label_21") |
|||
self.formLayout_2.setWidget(10, QtWidgets.QFormLayout.LabelRole, self.label_21) |
|||
self.dynamics_ffff = QtWidgets.QSpinBox(self.widgetLeft) |
|||
self.dynamics_ffff.setMaximum(127) |
|||
self.dynamics_ffff.setObjectName("dynamics_ffff") |
|||
self.formLayout_2.setWidget(10, QtWidgets.QFormLayout.FieldRole, self.dynamics_ffff) |
|||
self.horizontalLayout_6.addWidget(self.widgetLeft) |
|||
self.widgetRight = QtWidgets.QWidget(self.widget) |
|||
self.widgetRight.setObjectName("widgetRight") |
|||
self.formLayout_3 = QtWidgets.QFormLayout(self.widgetRight) |
|||
self.formLayout_3.setObjectName("formLayout_3") |
|||
self.label_12 = QtWidgets.QLabel(self.widgetRight) |
|||
self.label_12.setObjectName("label_12") |
|||
self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_12) |
|||
self.dynamics_tacet = QtWidgets.QSpinBox(self.widgetRight) |
|||
self.dynamics_tacet.setMaximum(127) |
|||
self.dynamics_tacet.setObjectName("dynamics_tacet") |
|||
self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.dynamics_tacet) |
|||
self.label_22 = QtWidgets.QLabel(self.widgetRight) |
|||
self.label_22.setObjectName("label_22") |
|||
self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_22) |
|||
self.dynamics_custom = QtWidgets.QSpinBox(self.widgetRight) |
|||
self.dynamics_custom.setMaximum(127) |
|||
self.dynamics_custom.setObjectName("dynamics_custom") |
|||
self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.dynamics_custom) |
|||
self.label_23 = QtWidgets.QLabel(self.widgetRight) |
|||
self.label_23.setObjectName("label_23") |
|||
self.formLayout_3.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_23) |
|||
self.dynamics_sfz = QtWidgets.QSpinBox(self.widgetRight) |
|||
self.dynamics_sfz.setMaximum(127) |
|||
self.dynamics_sfz.setObjectName("dynamics_sfz") |
|||
self.formLayout_3.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.dynamics_sfz) |
|||
self.label_24 = QtWidgets.QLabel(self.widgetRight) |
|||
self.label_24.setObjectName("label_24") |
|||
self.formLayout_3.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_24) |
|||
self.dynamics_sf = QtWidgets.QSpinBox(self.widgetRight) |
|||
self.dynamics_sf.setMaximum(127) |
|||
self.dynamics_sf.setObjectName("dynamics_sf") |
|||
self.formLayout_3.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.dynamics_sf) |
|||
self.label_25 = QtWidgets.QLabel(self.widgetRight) |
|||
self.label_25.setObjectName("label_25") |
|||
self.formLayout_3.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_25) |
|||
self.dynamics_sff = QtWidgets.QSpinBox(self.widgetRight) |
|||
self.dynamics_sff.setMaximum(127) |
|||
self.dynamics_sff.setObjectName("dynamics_sff") |
|||
self.formLayout_3.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.dynamics_sff) |
|||
self.label_26 = QtWidgets.QLabel(self.widgetRight) |
|||
self.label_26.setObjectName("label_26") |
|||
self.formLayout_3.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.label_26) |
|||
self.dynamics_fp = QtWidgets.QSpinBox(self.widgetRight) |
|||
self.dynamics_fp.setMaximum(127) |
|||
self.dynamics_fp.setObjectName("dynamics_fp") |
|||
self.formLayout_3.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.dynamics_fp) |
|||
self.label_27 = QtWidgets.QLabel(self.widgetRight) |
|||
self.label_27.setObjectName("label_27") |
|||
self.formLayout_3.setWidget(7, QtWidgets.QFormLayout.LabelRole, self.label_27) |
|||
self.dynamics_sp = QtWidgets.QSpinBox(self.widgetRight) |
|||
self.dynamics_sp.setMaximum(127) |
|||
self.dynamics_sp.setObjectName("dynamics_sp") |
|||
self.formLayout_3.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.dynamics_sp) |
|||
self.label_28 = QtWidgets.QLabel(self.widgetRight) |
|||
self.label_28.setObjectName("label_28") |
|||
self.formLayout_3.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.label_28) |
|||
self.dynamics_spp = QtWidgets.QSpinBox(self.widgetRight) |
|||
self.dynamics_spp.setMaximum(127) |
|||
self.dynamics_spp.setObjectName("dynamics_spp") |
|||
self.formLayout_3.setWidget(8, QtWidgets.QFormLayout.FieldRole, self.dynamics_spp) |
|||
self.horizontalLayout_6.addWidget(self.widgetRight) |
|||
spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) |
|||
self.horizontalLayout_6.addItem(spacerItem3) |
|||
self.verticalLayout_3.addWidget(self.widget) |
|||
self.buttonResetDynamics = QtWidgets.QPushButton(self.tab_2) |
|||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed) |
|||
sizePolicy.setHorizontalStretch(0) |
|||
sizePolicy.setVerticalStretch(0) |
|||
sizePolicy.setHeightForWidth(self.buttonResetDynamics.sizePolicy().hasHeightForWidth()) |
|||
self.buttonResetDynamics.setSizePolicy(sizePolicy) |
|||
self.buttonResetDynamics.setObjectName("buttonResetDynamics") |
|||
self.verticalLayout_3.addWidget(self.buttonResetDynamics) |
|||
self.advancedContent.addTab(self.tab_2, "") |
|||
self.verticalLayout_2.addWidget(self.advancedContent) |
|||
self.verticalLayout.addWidget(self.advanced) |
|||
|
|||
self.retranslateUi(trackGroupWidget) |
|||
self.advancedContent.setCurrentIndex(0) |
|||
QtCore.QMetaObject.connectSlotsByName(trackGroupWidget) |
|||
trackGroupWidget.setTabOrder(self.nameLineEdit, self.upbeatSpinBox) |
|||
trackGroupWidget.setTabOrder(self.upbeatSpinBox, self.callTickWidget) |
|||
trackGroupWidget.setTabOrder(self.callTickWidget, self.doubleTrackCheckbox) |
|||
trackGroupWidget.setTabOrder(self.doubleTrackCheckbox, self.visibleCheckbox) |
|||
trackGroupWidget.setTabOrder(self.visibleCheckbox, self.midiChannelSpinBox) |
|||
trackGroupWidget.setTabOrder(self.midiChannelSpinBox, self.ccChannelsPushButton) |
|||
trackGroupWidget.setTabOrder(self.ccChannelsPushButton, self.midiProgramSpinBox) |
|||
trackGroupWidget.setTabOrder(self.midiProgramSpinBox, self.midiBankMsbSpinBox) |
|||
trackGroupWidget.setTabOrder(self.midiBankMsbSpinBox, self.midiTransposeSpinBox) |
|||
trackGroupWidget.setTabOrder(self.midiTransposeSpinBox, self.midiBankLsbSpinBox) |
|||
trackGroupWidget.setTabOrder(self.midiBankLsbSpinBox, self.deleteButton) |
|||
trackGroupWidget.setTabOrder(self.deleteButton, self.instrumentName) |
|||
trackGroupWidget.setTabOrder(self.instrumentName, self.shortInstrumentName) |
|||
trackGroupWidget.setTabOrder(self.shortInstrumentName, self.advanced) |
|||
trackGroupWidget.setTabOrder(self.advanced, self.advancedContent) |
|||
trackGroupWidget.setTabOrder(self.advancedContent, self.defaultOn) |
|||
trackGroupWidget.setTabOrder(self.defaultOn, self.defaultOff) |
|||
trackGroupWidget.setTabOrder(self.defaultOff, self.staccatoOn) |
|||
trackGroupWidget.setTabOrder(self.staccatoOn, self.staccatoOff) |
|||
trackGroupWidget.setTabOrder(self.staccatoOff, self.tenutoOn) |
|||
trackGroupWidget.setTabOrder(self.tenutoOn, self.tenutoOff) |
|||
trackGroupWidget.setTabOrder(self.tenutoOff, self.legatoOn) |
|||
trackGroupWidget.setTabOrder(self.legatoOn, self.legatoOff) |
|||
trackGroupWidget.setTabOrder(self.legatoOff, self.buttonResetDurations) |
|||
trackGroupWidget.setTabOrder(self.buttonResetDurations, self.dynamics_ppppp) |
|||
trackGroupWidget.setTabOrder(self.dynamics_ppppp, self.dynamics_pppp) |
|||
trackGroupWidget.setTabOrder(self.dynamics_pppp, self.dynamics_ppp) |
|||
trackGroupWidget.setTabOrder(self.dynamics_ppp, self.dynamics_pp) |
|||
trackGroupWidget.setTabOrder(self.dynamics_pp, self.dynamics_p) |
|||
trackGroupWidget.setTabOrder(self.dynamics_p, self.dynamics_mp) |
|||
trackGroupWidget.setTabOrder(self.dynamics_mp, self.dynamics_mf) |
|||
trackGroupWidget.setTabOrder(self.dynamics_mf, self.dynamics_f) |
|||
trackGroupWidget.setTabOrder(self.dynamics_f, self.dynamics_ff) |
|||
trackGroupWidget.setTabOrder(self.dynamics_ff, self.dynamics_fff) |
|||
trackGroupWidget.setTabOrder(self.dynamics_fff, self.dynamics_ffff) |
|||
trackGroupWidget.setTabOrder(self.dynamics_ffff, self.dynamics_tacet) |
|||
trackGroupWidget.setTabOrder(self.dynamics_tacet, self.dynamics_custom) |
|||
trackGroupWidget.setTabOrder(self.dynamics_custom, self.dynamics_sfz) |
|||
trackGroupWidget.setTabOrder(self.dynamics_sfz, self.dynamics_sf) |
|||
trackGroupWidget.setTabOrder(self.dynamics_sf, self.dynamics_sff) |
|||
trackGroupWidget.setTabOrder(self.dynamics_sff, self.dynamics_fp) |
|||
trackGroupWidget.setTabOrder(self.dynamics_fp, self.dynamics_sp) |
|||
trackGroupWidget.setTabOrder(self.dynamics_sp, self.dynamics_spp) |
|||
trackGroupWidget.setTabOrder(self.dynamics_spp, self.buttonResetDynamics) |
|||
|
|||
def retranslateUi(self, trackGroupWidget): |
|||
_translate = QtCore.QCoreApplication.translate |
|||
trackGroupWidget.setWindowTitle(_translate("trackGroupWidget", "GroupBox")) |
|||
trackGroupWidget.setTitle(_translate("trackGroupWidget", "GroupBox")) |
|||
self.upbeatSpinBox.setSuffix(_translate("trackGroupWidget", " Upbeat Ticks")) |
|||
self.callTickWidget.setText(_translate("trackGroupWidget", "𝅘𝅥𝅮 ")) |
|||
self.doubleTrackCheckbox.setText(_translate("trackGroupWidget", "Double Track")) |
|||
self.visibleCheckbox.setText(_translate("trackGroupWidget", "Visible")) |
|||
self.midiChannelSpinBox.setPrefix(_translate("trackGroupWidget", "Channel ")) |
|||
self.ccChannelsPushButton.setText(_translate("trackGroupWidget", "CC Channels")) |
|||
self.midiProgramSpinBox.setPrefix(_translate("trackGroupWidget", "Program ")) |
|||
self.midiBankMsbSpinBox.setPrefix(_translate("trackGroupWidget", "Bank MSB")) |
|||
self.midiTransposeSpinBox.setPrefix(_translate("trackGroupWidget", "Transpose ")) |
|||
self.midiBankLsbSpinBox.setPrefix(_translate("trackGroupWidget", "Bank LSB")) |
|||
self.deleteButton.setText(_translate("trackGroupWidget", "Delete")) |
|||
self.label.setText(_translate("trackGroupWidget", "Instrument Name")) |
|||
self.label_2.setText(_translate("trackGroupWidget", "Short Instrument Name")) |
|||
self.advanced.setTitle(_translate("trackGroupWidget", "Advanced")) |
|||
self.label_3.setText(_translate("trackGroupWidget", "Derfault On")) |
|||
self.label_4.setText(_translate("trackGroupWidget", "Default Off")) |
|||
self.label_6.setText(_translate("trackGroupWidget", "Staccato On")) |
|||
self.label_5.setText(_translate("trackGroupWidget", "Staccato Off")) |
|||
self.label_8.setText(_translate("trackGroupWidget", "Tenuto On")) |
|||
self.label_9.setText(_translate("trackGroupWidget", "Tenuto Off")) |
|||
self.label_10.setText(_translate("trackGroupWidget", "Legato On")) |
|||
self.label_7.setText(_translate("trackGroupWidget", "Legato Off")) |
|||
self.buttonResetDurations.setText(_translate("trackGroupWidget", "Reset to Default Duration Values")) |
|||
self.advancedContent.setTabText(self.advancedContent.indexOf(self.tab), _translate("trackGroupWidget", "Duration Offsets")) |
|||
self.label_11.setText(_translate("trackGroupWidget", "ppppp")) |
|||
self.label_13.setText(_translate("trackGroupWidget", "pppp")) |
|||
self.label_29.setText(_translate("trackGroupWidget", "ppp")) |
|||
self.label_14.setText(_translate("trackGroupWidget", "pp")) |
|||
self.label_15.setText(_translate("trackGroupWidget", "p")) |
|||
self.label_16.setText(_translate("trackGroupWidget", "mp")) |
|||
self.label_17.setText(_translate("trackGroupWidget", "mf")) |
|||
self.label_18.setText(_translate("trackGroupWidget", "f")) |
|||
self.label_19.setText(_translate("trackGroupWidget", "ff")) |
|||
self.label_20.setText(_translate("trackGroupWidget", "fff")) |
|||
self.label_21.setText(_translate("trackGroupWidget", "ffff")) |
|||
self.label_12.setText(_translate("trackGroupWidget", "tacet")) |
|||
self.label_22.setText(_translate("trackGroupWidget", "custom")) |
|||
self.label_23.setText(_translate("trackGroupWidget", "sfz")) |
|||
self.label_24.setText(_translate("trackGroupWidget", "sf")) |
|||
self.label_25.setText(_translate("trackGroupWidget", "sff")) |
|||
self.label_26.setText(_translate("trackGroupWidget", "fp")) |
|||
self.label_27.setText(_translate("trackGroupWidget", "sp")) |
|||
self.label_28.setText(_translate("trackGroupWidget", "spp")) |
|||
self.buttonResetDynamics.setText(_translate("trackGroupWidget", "Reset to Default Dynamic Values")) |
|||
self.advancedContent.setTabText(self.advancedContent.indexOf(self.tab_2), _translate("trackGroupWidget", "Dynamic Signature Definitions")) |
|||
|
@ -0,0 +1,868 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<ui version="4.0"> |
|||
<class>trackGroupWidget</class> |
|||
<widget class="QGroupBox" name="trackGroupWidget"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>0</x> |
|||
<y>0</y> |
|||
<width>1374</width> |
|||
<height>530</height> |
|||
</rect> |
|||
</property> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> |
|||
<horstretch>0</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
<property name="maximumSize"> |
|||
<size> |
|||
<width>16777215</width> |
|||
<height>900</height> |
|||
</size> |
|||
</property> |
|||
<property name="windowTitle"> |
|||
<string>GroupBox</string> |
|||
</property> |
|||
<property name="title"> |
|||
<string>GroupBox</string> |
|||
</property> |
|||
<layout class="QVBoxLayout" name="verticalLayout"> |
|||
<property name="spacing"> |
|||
<number>3</number> |
|||
</property> |
|||
<property name="leftMargin"> |
|||
<number>3</number> |
|||
</property> |
|||
<property name="topMargin"> |
|||
<number>3</number> |
|||
</property> |
|||
<property name="rightMargin"> |
|||
<number>3</number> |
|||
</property> |
|||
<property name="bottomMargin"> |
|||
<number>3</number> |
|||
</property> |
|||
<item> |
|||
<widget class="QWidget" name="track" native="true"> |
|||
<property name="maximumSize"> |
|||
<size> |
|||
<width>16777215</width> |
|||
<height>45</height> |
|||
</size> |
|||
</property> |
|||
<layout class="QHBoxLayout" name="horizontalLayout"> |
|||
<property name="spacing"> |
|||
<number>3</number> |
|||
</property> |
|||
<property name="leftMargin"> |
|||
<number>3</number> |
|||
</property> |
|||
<property name="topMargin"> |
|||
<number>3</number> |
|||
</property> |
|||
<property name="rightMargin"> |
|||
<number>3</number> |
|||
</property> |
|||
<property name="bottomMargin"> |
|||
<number>3</number> |
|||
</property> |
|||
<item> |
|||
<widget class="QLineEdit" name="nameLineEdit"> |
|||
<property name="enabled"> |
|||
<bool>true</bool> |
|||
</property> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> |
|||
<horstretch>0</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
<property name="minimumSize"> |
|||
<size> |
|||
<width>200</width> |
|||
<height>0</height> |
|||
</size> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QFrame" name="frame"> |
|||
<property name="frameShape"> |
|||
<enum>QFrame::Box</enum> |
|||
</property> |
|||
<property name="frameShadow"> |
|||
<enum>QFrame::Sunken</enum> |
|||
</property> |
|||
<layout class="QHBoxLayout" name="horizontalLayout_3"> |
|||
<property name="spacing"> |
|||
<number>0</number> |
|||
</property> |
|||
<property name="leftMargin"> |
|||
<number>3</number> |
|||
</property> |
|||
<property name="topMargin"> |
|||
<number>0</number> |
|||
</property> |
|||
<property name="rightMargin"> |
|||
<number>3</number> |
|||
</property> |
|||
<property name="bottomMargin"> |
|||
<number>0</number> |
|||
</property> |
|||
<item> |
|||
<widget class="QSpinBox" name="upbeatSpinBox"> |
|||
<property name="suffix"> |
|||
<string> Upbeat Ticks</string> |
|||
</property> |
|||
<property name="prefix"> |
|||
<string/> |
|||
</property> |
|||
<property name="maximum"> |
|||
<number>999999999</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QPushButton" name="callTickWidget"> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed"> |
|||
<horstretch>0</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
<property name="maximumSize"> |
|||
<size> |
|||
<width>25</width> |
|||
<height>16777215</height> |
|||
</size> |
|||
</property> |
|||
<property name="text"> |
|||
<string>𝅘𝅥𝅮 </string> |
|||
</property> |
|||
<property name="flat"> |
|||
<bool>false</bool> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QCheckBox" name="doubleTrackCheckbox"> |
|||
<property name="text"> |
|||
<string>Double Track</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QCheckBox" name="visibleCheckbox"> |
|||
<property name="text"> |
|||
<string>Visible</string> |
|||
</property> |
|||
<property name="checked"> |
|||
<bool>true</bool> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QFrame" name="frame_2"> |
|||
<property name="frameShape"> |
|||
<enum>QFrame::Box</enum> |
|||
</property> |
|||
<property name="frameShadow"> |
|||
<enum>QFrame::Sunken</enum> |
|||
</property> |
|||
<layout class="QHBoxLayout" name="horizontalLayout_4"> |
|||
<property name="spacing"> |
|||
<number>3</number> |
|||
</property> |
|||
<property name="leftMargin"> |
|||
<number>3</number> |
|||
</property> |
|||
<property name="topMargin"> |
|||
<number>0</number> |
|||
</property> |
|||
<property name="rightMargin"> |
|||
<number>3</number> |
|||
</property> |
|||
<property name="bottomMargin"> |
|||
<number>0</number> |
|||
</property> |
|||
<item> |
|||
<widget class="QSpinBox" name="midiChannelSpinBox"> |
|||
<property name="prefix"> |
|||
<string>Channel </string> |
|||
</property> |
|||
<property name="maximum"> |
|||
<number>15</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QPushButton" name="ccChannelsPushButton"> |
|||
<property name="text"> |
|||
<string>CC Channels</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QSpinBox" name="midiProgramSpinBox"> |
|||
<property name="prefix"> |
|||
<string>Program </string> |
|||
</property> |
|||
<property name="minimum"> |
|||
<number>-1</number> |
|||
</property> |
|||
<property name="maximum"> |
|||
<number>127</number> |
|||
</property> |
|||
<property name="value"> |
|||
<number>-1</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QSpinBox" name="midiBankMsbSpinBox"> |
|||
<property name="enabled"> |
|||
<bool>false</bool> |
|||
</property> |
|||
<property name="prefix"> |
|||
<string>Bank MSB</string> |
|||
</property> |
|||
<property name="minimum"> |
|||
<number>0</number> |
|||
</property> |
|||
<property name="maximum"> |
|||
<number>127</number> |
|||
</property> |
|||
<property name="value"> |
|||
<number>0</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QSpinBox" name="midiTransposeSpinBox"> |
|||
<property name="prefix"> |
|||
<string>Transpose </string> |
|||
</property> |
|||
<property name="minimum"> |
|||
<number>-127</number> |
|||
</property> |
|||
<property name="maximum"> |
|||
<number>128</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QSpinBox" name="midiBankLsbSpinBox"> |
|||
<property name="enabled"> |
|||
<bool>false</bool> |
|||
</property> |
|||
<property name="prefix"> |
|||
<string>Bank LSB</string> |
|||
</property> |
|||
<property name="minimum"> |
|||
<number>0</number> |
|||
</property> |
|||
<property name="maximum"> |
|||
<number>127</number> |
|||
</property> |
|||
<property name="value"> |
|||
<number>0</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QPushButton" name="deleteButton"> |
|||
<property name="text"> |
|||
<string>Delete</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<spacer name="horizontalSpacer"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Horizontal</enum> |
|||
</property> |
|||
<property name="sizeHint" stdset="0"> |
|||
<size> |
|||
<width>640</width> |
|||
<height>477</height> |
|||
</size> |
|||
</property> |
|||
</spacer> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<layout class="QHBoxLayout" name="horizontalLayout_2"> |
|||
<item> |
|||
<widget class="QLabel" name="label"> |
|||
<property name="text"> |
|||
<string>Instrument Name</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QLineEdit" name="instrumentName"> |
|||
<property name="maxLength"> |
|||
<number>32767</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QLabel" name="label_2"> |
|||
<property name="text"> |
|||
<string>Short Instrument Name</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QLineEdit" name="shortInstrumentName"> |
|||
<property name="maxLength"> |
|||
<number>5</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<spacer name="horizontalSpacer_2"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Horizontal</enum> |
|||
</property> |
|||
<property name="sizeHint" stdset="0"> |
|||
<size> |
|||
<width>40</width> |
|||
<height>20</height> |
|||
</size> |
|||
</property> |
|||
</spacer> |
|||
</item> |
|||
</layout> |
|||
</item> |
|||
<item> |
|||
<widget class="QGroupBox" name="advanced"> |
|||
<property name="title"> |
|||
<string>Advanced</string> |
|||
</property> |
|||
<property name="checkable"> |
|||
<bool>true</bool> |
|||
</property> |
|||
<layout class="QVBoxLayout" name="verticalLayout_2"> |
|||
<item> |
|||
<widget class="QTabWidget" name="advancedContent"> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum"> |
|||
<horstretch>0</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
<property name="currentIndex"> |
|||
<number>0</number> |
|||
</property> |
|||
<widget class="QWidget" name="tab"> |
|||
<attribute name="title"> |
|||
<string>Duration Offsets</string> |
|||
</attribute> |
|||
<layout class="QVBoxLayout" name="verticalLayout_4"> |
|||
<item> |
|||
<widget class="QWidget" name="widget_3" native="true"> |
|||
<layout class="QHBoxLayout" name="horizontalLayout_5"> |
|||
<item> |
|||
<widget class="QWidget" name="widget_2" native="true"> |
|||
<layout class="QFormLayout" name="formLayout_4"> |
|||
<item row="7" column="0"> |
|||
<widget class="QLabel" name="label_3"> |
|||
<property name="text"> |
|||
<string>Derfault On</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="7" column="1"> |
|||
<widget class="QLineEdit" name="defaultOn"/> |
|||
</item> |
|||
<item row="8" column="0"> |
|||
<widget class="QLabel" name="label_4"> |
|||
<property name="text"> |
|||
<string>Default Off</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="8" column="1"> |
|||
<widget class="QLineEdit" name="defaultOff"/> |
|||
</item> |
|||
<item row="9" column="0"> |
|||
<widget class="QLabel" name="label_6"> |
|||
<property name="text"> |
|||
<string>Staccato On</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="9" column="1"> |
|||
<widget class="QLineEdit" name="staccatoOn"/> |
|||
</item> |
|||
<item row="10" column="0"> |
|||
<widget class="QLabel" name="label_5"> |
|||
<property name="text"> |
|||
<string>Staccato Off</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="10" column="1"> |
|||
<widget class="QLineEdit" name="staccatoOff"/> |
|||
</item> |
|||
<item row="11" column="0"> |
|||
<widget class="QLabel" name="label_8"> |
|||
<property name="text"> |
|||
<string>Tenuto On</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="11" column="1"> |
|||
<widget class="QLineEdit" name="tenutoOn"/> |
|||
</item> |
|||
<item row="12" column="0"> |
|||
<widget class="QLabel" name="label_9"> |
|||
<property name="text"> |
|||
<string>Tenuto Off</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="12" column="1"> |
|||
<widget class="QLineEdit" name="tenutoOff"/> |
|||
</item> |
|||
<item row="13" column="0"> |
|||
<widget class="QLabel" name="label_10"> |
|||
<property name="text"> |
|||
<string>Legato On</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="13" column="1"> |
|||
<widget class="QLineEdit" name="legatoOn"/> |
|||
</item> |
|||
<item row="14" column="0"> |
|||
<widget class="QLabel" name="label_7"> |
|||
<property name="text"> |
|||
<string>Legato Off</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="14" column="1"> |
|||
<widget class="QLineEdit" name="legatoOff"/> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<spacer name="horizontalSpacer_4"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Horizontal</enum> |
|||
</property> |
|||
<property name="sizeHint" stdset="0"> |
|||
<size> |
|||
<width>643</width> |
|||
<height>20</height> |
|||
</size> |
|||
</property> |
|||
</spacer> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QPushButton" name="buttonResetDurations"> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed"> |
|||
<horstretch>0</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
<property name="text"> |
|||
<string>Reset to Default Duration Values</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
<widget class="QWidget" name="tab_2"> |
|||
<attribute name="title"> |
|||
<string>Dynamic Signature Definitions</string> |
|||
</attribute> |
|||
<layout class="QVBoxLayout" name="verticalLayout_3"> |
|||
<item> |
|||
<widget class="QWidget" name="widget" native="true"> |
|||
<layout class="QHBoxLayout" name="horizontalLayout_6"> |
|||
<item> |
|||
<widget class="QWidget" name="widget_4" native="true"> |
|||
<layout class="QFormLayout" name="formLayout"> |
|||
<item row="0" column="0"> |
|||
<widget class="QLabel" name="label_11"> |
|||
<property name="text"> |
|||
<string>ppppp</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="0" column="1"> |
|||
<widget class="QSpinBox" name="dynamics_ppppp"> |
|||
<property name="maximum"> |
|||
<number>127</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="1" column="0"> |
|||
<widget class="QLabel" name="label_13"> |
|||
<property name="text"> |
|||
<string>pppp</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="1" column="1"> |
|||
<widget class="QSpinBox" name="dynamics_pppp"> |
|||
<property name="maximum"> |
|||
<number>127</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="2" column="0"> |
|||
<widget class="QLabel" name="label_29"> |
|||
<property name="text"> |
|||
<string>ppp</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="2" column="1"> |
|||
<widget class="QSpinBox" name="dynamics_ppp"/> |
|||
</item> |
|||
<item row="3" column="0"> |
|||
<widget class="QLabel" name="label_14"> |
|||
<property name="text"> |
|||
<string>pp</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="3" column="1"> |
|||
<widget class="QSpinBox" name="dynamics_pp"> |
|||
<property name="maximum"> |
|||
<number>127</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="4" column="0"> |
|||
<widget class="QLabel" name="label_15"> |
|||
<property name="text"> |
|||
<string>p</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="4" column="1"> |
|||
<widget class="QSpinBox" name="dynamics_p"> |
|||
<property name="maximum"> |
|||
<number>127</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="5" column="0"> |
|||
<widget class="QLabel" name="label_16"> |
|||
<property name="text"> |
|||
<string>mp</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="5" column="1"> |
|||
<widget class="QSpinBox" name="dynamics_mp"> |
|||
<property name="maximum"> |
|||
<number>127</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QWidget" name="widgetLeft" native="true"> |
|||
<layout class="QFormLayout" name="formLayout_2"> |
|||
<item row="2" column="0"> |
|||
<widget class="QLabel" name="label_17"> |
|||
<property name="text"> |
|||
<string>mf</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="2" column="1"> |
|||
<widget class="QSpinBox" name="dynamics_mf"> |
|||
<property name="maximum"> |
|||
<number>127</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="5" column="0"> |
|||
<widget class="QLabel" name="label_18"> |
|||
<property name="text"> |
|||
<string>f</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="5" column="1"> |
|||
<widget class="QSpinBox" name="dynamics_f"> |
|||
<property name="maximum"> |
|||
<number>127</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="8" column="0"> |
|||
<widget class="QLabel" name="label_19"> |
|||
<property name="text"> |
|||
<string>ff</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="8" column="1"> |
|||
<widget class="QSpinBox" name="dynamics_ff"> |
|||
<property name="maximum"> |
|||
<number>127</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="9" column="0"> |
|||
<widget class="QLabel" name="label_20"> |
|||
<property name="text"> |
|||
<string>fff</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="9" column="1"> |
|||
<widget class="QSpinBox" name="dynamics_fff"> |
|||
<property name="maximum"> |
|||
<number>127</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="10" column="0"> |
|||
<widget class="QLabel" name="label_21"> |
|||
<property name="text"> |
|||
<string>ffff</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="10" column="1"> |
|||
<widget class="QSpinBox" name="dynamics_ffff"> |
|||
<property name="maximum"> |
|||
<number>127</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QWidget" name="widgetRight" native="true"> |
|||
<layout class="QFormLayout" name="formLayout_3"> |
|||
<item row="0" column="0"> |
|||
<widget class="QLabel" name="label_12"> |
|||
<property name="text"> |
|||
<string>tacet</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="0" column="1"> |
|||
<widget class="QSpinBox" name="dynamics_tacet"> |
|||
<property name="maximum"> |
|||
<number>127</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="1" column="0"> |
|||
<widget class="QLabel" name="label_22"> |
|||
<property name="text"> |
|||
<string>custom</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="1" column="1"> |
|||
<widget class="QSpinBox" name="dynamics_custom"> |
|||
<property name="maximum"> |
|||
<number>127</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="2" column="0"> |
|||
<widget class="QLabel" name="label_23"> |
|||
<property name="text"> |
|||
<string>sfz</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="2" column="1"> |
|||
<widget class="QSpinBox" name="dynamics_sfz"> |
|||
<property name="maximum"> |
|||
<number>127</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="3" column="0"> |
|||
<widget class="QLabel" name="label_24"> |
|||
<property name="text"> |
|||
<string>sf</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="3" column="1"> |
|||
<widget class="QSpinBox" name="dynamics_sf"> |
|||
<property name="maximum"> |
|||
<number>127</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="4" column="0"> |
|||
<widget class="QLabel" name="label_25"> |
|||
<property name="text"> |
|||
<string>sff</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="4" column="1"> |
|||
<widget class="QSpinBox" name="dynamics_sff"> |
|||
<property name="maximum"> |
|||
<number>127</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="6" column="0"> |
|||
<widget class="QLabel" name="label_26"> |
|||
<property name="text"> |
|||
<string>fp</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="6" column="1"> |
|||
<widget class="QSpinBox" name="dynamics_fp"> |
|||
<property name="maximum"> |
|||
<number>127</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="7" column="0"> |
|||
<widget class="QLabel" name="label_27"> |
|||
<property name="text"> |
|||
<string>sp</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="7" column="1"> |
|||
<widget class="QSpinBox" name="dynamics_sp"> |
|||
<property name="maximum"> |
|||
<number>127</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="8" column="0"> |
|||
<widget class="QLabel" name="label_28"> |
|||
<property name="text"> |
|||
<string>spp</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="8" column="1"> |
|||
<widget class="QSpinBox" name="dynamics_spp"> |
|||
<property name="maximum"> |
|||
<number>127</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<spacer name="horizontalSpacer_3"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Horizontal</enum> |
|||
</property> |
|||
<property name="sizeHint" stdset="0"> |
|||
<size> |
|||
<width>40</width> |
|||
<height>20</height> |
|||
</size> |
|||
</property> |
|||
</spacer> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
</item> |
|||
<item> |
|||
<widget class="QPushButton" name="buttonResetDynamics"> |
|||
<property name="sizePolicy"> |
|||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed"> |
|||
<horstretch>0</horstretch> |
|||
<verstretch>0</verstretch> |
|||
</sizepolicy> |
|||
</property> |
|||
<property name="text"> |
|||
<string>Reset to Default Dynamic Values</string> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
</widget> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
<tabstops> |
|||
<tabstop>nameLineEdit</tabstop> |
|||
<tabstop>upbeatSpinBox</tabstop> |
|||
<tabstop>callTickWidget</tabstop> |
|||
<tabstop>doubleTrackCheckbox</tabstop> |
|||
<tabstop>visibleCheckbox</tabstop> |
|||
<tabstop>midiChannelSpinBox</tabstop> |
|||
<tabstop>ccChannelsPushButton</tabstop> |
|||
<tabstop>midiProgramSpinBox</tabstop> |
|||
<tabstop>midiBankMsbSpinBox</tabstop> |
|||
<tabstop>midiTransposeSpinBox</tabstop> |
|||
<tabstop>midiBankLsbSpinBox</tabstop> |
|||
<tabstop>deleteButton</tabstop> |
|||
<tabstop>instrumentName</tabstop> |
|||
<tabstop>shortInstrumentName</tabstop> |
|||
<tabstop>advanced</tabstop> |
|||
<tabstop>advancedContent</tabstop> |
|||
<tabstop>defaultOn</tabstop> |
|||
<tabstop>defaultOff</tabstop> |
|||
<tabstop>staccatoOn</tabstop> |
|||
<tabstop>staccatoOff</tabstop> |
|||
<tabstop>tenutoOn</tabstop> |
|||
<tabstop>tenutoOff</tabstop> |
|||
<tabstop>legatoOn</tabstop> |
|||
<tabstop>legatoOff</tabstop> |
|||
<tabstop>buttonResetDurations</tabstop> |
|||
<tabstop>dynamics_ppppp</tabstop> |
|||
<tabstop>dynamics_pppp</tabstop> |
|||
<tabstop>dynamics_ppp</tabstop> |
|||
<tabstop>dynamics_pp</tabstop> |
|||
<tabstop>dynamics_p</tabstop> |
|||
<tabstop>dynamics_mp</tabstop> |
|||
<tabstop>dynamics_mf</tabstop> |
|||
<tabstop>dynamics_f</tabstop> |
|||
<tabstop>dynamics_ff</tabstop> |
|||
<tabstop>dynamics_fff</tabstop> |
|||
<tabstop>dynamics_ffff</tabstop> |
|||
<tabstop>dynamics_tacet</tabstop> |
|||
<tabstop>dynamics_custom</tabstop> |
|||
<tabstop>dynamics_sfz</tabstop> |
|||
<tabstop>dynamics_sf</tabstop> |
|||
<tabstop>dynamics_sff</tabstop> |
|||
<tabstop>dynamics_fp</tabstop> |
|||
<tabstop>dynamics_sp</tabstop> |
|||
<tabstop>dynamics_spp</tabstop> |
|||
<tabstop>buttonResetDynamics</tabstop> |
|||
</tabstops> |
|||
<resources/> |
|||
<connections/> |
|||
</ui> |
@ -0,0 +1,571 @@ |
|||
#! /usr/bin/env python3 |
|||
# -*- coding: utf-8 -*- |
|||
""" |
|||
Copyright 2017, Nils Hilbricht, Germany ( https://www.hilbricht.net ) |
|||
|
|||
This file is part of Laborejo ( https://www.laborejo.org ) |
|||
|
|||
Laborejo is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
""" |
|||
|
|||
from PyQt5 import QtCore, QtGui, QtSvg, QtWidgets |
|||
from .constantsAndConfigs import constantsAndConfigs |
|||
from template.qtgui.helper import stringToColor, removeInstancesFromScene, callContextMenu, stretchLine, stretchRect |
|||
import engine.api as api |
|||
|
|||
class CCPath(QtWidgets.QGraphicsRectItem): |
|||
""" |
|||
A CCPath only exists when the backend track has a cc-part activated. |
|||
Since each track has 128 potential CC paths we don't create and |
|||
save a bunch of empty ones, neither in the backend nor here. |
|||
|
|||
Therefore: you cannot display a message "click here to create |
|||
a CC track/block" here in the CC Path since the CCPath does not |
|||
exist at this point in time. Thus this button is created in the track. One button for all CCs. |
|||
|
|||
There are two item types. Movable, or handle-points, |
|||
and non-movable: interpolation-points. |
|||
|
|||
The backend only knows handle points and a function which |
|||
interpolates until the next point. |
|||
|
|||
However, that results only in CC values 0-128 anyway. So |
|||
we use these directly. |
|||
|
|||
We don't lie to the user. There is no smooth curve, not even |
|||
a linear one, between CC values 65 and 66. It is just a step. |
|||
|
|||
So we show the steps.""" |
|||
|
|||
def __init__(self, parentGuiTrack, parentDataTrackId): |
|||
super().__init__(0, 0, 0, 0) |
|||
self.setAcceptHoverEvents(True) |
|||
#self.setFlags(QtWidgets.QGraphicsItem.ItemIsMovable|QtWidgets.QGraphicsItem.ItemSendsGeometryChanges|QtWidgets.QGraphicsItem.ItemIsFocusable|QtWidgets.QGraphicsItem.ItemIgnoresParentOpacity) |
|||
self.setFlags(QtWidgets.QGraphicsItem.ItemIsFocusable|QtWidgets.QGraphicsItem.ItemIgnoresParentOpacity) |
|||
#self.setBrush(QtCore.Qt.red) #debug |
|||
self.setPen(QtGui.QPen(QtCore.Qt.transparent)) |
|||
|
|||
self.parentDataTrackId = parentDataTrackId |
|||
self.parentGuiTrack = parentGuiTrack |
|||
self.userItems = [] |
|||
self.interpolatedItems = [] |
|||
self.other = [] |
|||
self.blockListCache = [] #backend dictExport items. updated through self.updateGraphBlockTrack |
|||
self.blockdictCache = {} #blockId:guiBlock updated through self.updateGraphBlockTrack |
|||
self.transparentBlockHandles = [] #transparentBlockHandles in correct order. updated through self.updateGraphBlockTrack |
|||
|
|||
def itemChange(self, changeEnum, value): |
|||
if changeEnum == QtWidgets.QGraphicsItem.ItemVisibleHasChanged: #12 |
|||
if self.isVisible(): |
|||
self.parentGuiTrack.universalCreateFirstCCBlock.hide() |
|||
else: |
|||
self.parentGuiTrack.universalCreateFirstCCBlock.show() |
|||
|
|||
return super().itemChange(changeEnum, value) |
|||
|
|||
def mousePressEvent(self, event): |
|||
if event.button() == 1: #QtCore.Qt.MouseButton.LeftButton |
|||
self.add(event.pos()) |
|||
|
|||
@property |
|||
def items(self): |
|||
return self.userItems + self.interpolatedItems + self.other |
|||
|
|||
#@property |
|||
#def transparentBlockHandles(self): |
|||
#return CCGraphTransparentBlock.instances This does not work because we need the blocks per track, not all of them in all tracks. |
|||
|
|||
def createGraphicItemsFromData(self, staticRepresentationList): |
|||
"""The tick position is, as always, a long tickvalue from the |
|||
backend. |
|||
The CC value, 0-127, has its default 0-line on the middle line |
|||
as all items, since this is the track root. Since CC 64 is the |
|||
middle value for CCs we shift all the points down by 64 so |
|||
the middle staff line becomes CC 64. |
|||
""" |
|||
for item in self.items: |
|||
self.scene().removeWhenIdle(item) |
|||
self.other = [] |
|||
self.userItems = [] |
|||
self.interpolatedItems = [] |
|||
|
|||
for point in staticRepresentationList: |
|||
if point["type"] == "interpolated": |
|||
p = CCInterpolatedPoint() |
|||
self.interpolatedItems.append(p) |
|||
self.userItems[-1].interpolatedItemsRight.append(p) |
|||
else: #user, lastInBlock or lastInTrack |
|||
p = CCUserPoint(self, point) |
|||
|
|||
if self.userItems: |
|||
p.interpolatedItemsLeft = self.userItems[-1].interpolatedItemsRight |
|||
|
|||
self.userItems.append(p) |
|||
|
|||
t = QtWidgets.QGraphicsSimpleTextItem(str(abs(point["value"]))) |
|||
t.setFont(constantsAndConfigs.theFont) |
|||
t.setParentItem(self) |
|||
t.setScale(0.75) |
|||
self.other.append(t) |
|||
t.setPos(point["position"] / constantsAndConfigs.ticksToPixelRatio, 29) |
|||
|
|||
p.setParentItem(self) |
|||
p.setPos(point["position"] / constantsAndConfigs.ticksToPixelRatio , (point["value"]+64)*0.4375) #value goes from -0 to -127 and are compressed to fit between two lines in the staff. |
|||
|
|||
|
|||
def updateGraphBlockTrack(self, staticRepresentationList): |
|||
"""Handles and visualizes block boundaries""" |
|||
#{"type" : "GraphBlock", "id":id(block), "name":block.name, "duration":block.duration, "position":tickCounter} |
|||
|
|||
self.blockListCache = staticRepresentationList #sorted list |
|||
self.blockdictCache = {} #reset |
|||
|
|||
for tbh in self.transparentBlockHandles: |
|||
tbh.setParentItem(None) |
|||
tbh.scene().removeWhenIdle(tbh) |
|||
self.transparentBlockHandles = [] #reset |
|||
|
|||
#removeInstancesFromScene(CCGraphTransparentBlock) #this removes ALL blocks in all tracks from the scene. don't. |
|||
for dictExportItem in staticRepresentationList: |
|||
guiBlock = CCGraphTransparentBlock(parent = self, staticExportItem = dictExportItem, x = 0, y = -28, w = dictExportItem["duration"] / constantsAndConfigs.ticksToPixelRatio, h = 2*28) |
|||
guiBlock.setParentItem(self) |
|||
guiBlock.setPos(dictExportItem["position"] / constantsAndConfigs.ticksToPixelRatio,0) |
|||
self.blockdictCache[dictExportItem["id"]] = guiBlock |
|||
self.transparentBlockHandles.append(guiBlock) |
|||
|
|||
#Use the last item of the loop above to draw length of the boundary lines. |
|||
maximumPixels = (dictExportItem["duration"] + dictExportItem["position"]) / constantsAndConfigs.ticksToPixelRatio |
|||
upperLine = QtWidgets.QGraphicsLineItem(0,-28,maximumPixels,-28) #x1, y1, x2, y2 |
|||
#upperLine.setPen(QtGui.QColor("red")) |
|||
upperLine.setParentItem(self) |
|||
self.other.append(upperLine) |
|||
lowerLine = QtWidgets.QGraphicsLineItem(0,28,maximumPixels,28) #x1, y1, x2, y2 |
|||
#lowerLine.setPen(QtGui.QColor("green")) |
|||
lowerLine.setParentItem(self) |
|||
self.other.append(lowerLine) |
|||
upperLine.setOpacity(0.3) |
|||
lowerLine.setOpacity(0.3) |
|||
upperLine.setPos(0,0) |
|||
lowerLine.setPos(0,0) |
|||
#for self.stretchXCoordinates |
|||
self.upperLine = upperLine |
|||
self.lowerLine = lowerLine |
|||
|
|||
self.setRect(0,-28,maximumPixels,2*28) |
|||
|
|||
def stretchXCoordinates(self, factor): |
|||
"""Reposition the items on the X axis. |
|||
Call goes through all parents/children, starting from ScoreView._stretchXCoordinates. |
|||
Docstring there.""" |
|||
stretchRect(self, factor) |
|||
stretchLine(self.upperLine, factor) |
|||
stretchLine(self.lowerLine, factor) |
|||
|
|||
for item in self.items: |
|||
item.setX(item.pos().x() * factor) |
|||
|
|||
for transparentBlockHandle in self.transparentBlockHandles: |
|||
transparentBlockHandle.setX(transparentBlockHandle.pos().x() * factor) |
|||
transparentBlockHandle.stretchXCoordinates(factor) |
|||
|
|||
def blocKByPosition(self, tickPositionAbsoluteBackendValue): |
|||
for block in self.blockListCache: |
|||
start = block["position"] |
|||
end = start + block["duration"] |
|||
if start <= tickPositionAbsoluteBackendValue < end: |
|||
return block["id"], block["position"] |
|||
return None #After the last block. |
|||
|
|||
def blockAt(self, qScenePosition): |
|||
"""For compatibility with track.blockAt |
|||
Can return a block or None for "after the last block" """ |
|||
backendPosition = qScenePosition * constantsAndConfigs.ticksToPixelRatio |
|||
result = self.blocKByPosition(backendPosition) |
|||
if result: |
|||
blockId, blockBackendPosition = result |
|||
assert blockId in self.blockdictCache |
|||
return self.blockdictCache[blockId] |
|||
else: |
|||
return None |
|||
|
|||
def getCCNumber(self): |
|||
"""The CC number is a dynamic value""" |
|||
for ccNumber, ccGraphTrack in self.parentGuiTrack.ccPaths.items(): |
|||
if ccGraphTrack is self: |
|||
return ccNumber |
|||
else: |
|||
raise ValueError("This Block is not in the ccPath dict of the GuiTrack""") |
|||
|
|||
|
|||
def getYAsBackendValue(self, y): |
|||
newCCValue = int(y / 0.4375 - 64) #value goes from -0 to -127 and are compressed to fit between two lines in the staff. |
|||
newCCValue = abs(newCCValue) |
|||
assert 0 <= newCCValue < 128 |
|||
return newCCValue |
|||
|
|||
def add(self, qPos): |
|||
"""Only activated through the hover area which gives are the |
|||
most control over where clicks are allowed. |
|||
Also there was a weird bug that the CCPath itself cannot |
|||
detect mouse clicks in the right position""" |
|||
y = qPos.y() |
|||
if -28 < y < 28: |
|||
sp = qPos.x() * constantsAndConfigs.ticksToPixelRatio |
|||
blockId, blockStartOffset = self.blocKByPosition(sp) #get the block before rounding. Otherwise rounding might result in a position higher than the blocks duration |
|||
if constantsAndConfigs.snapToGrid: |
|||
sp = round(sp / constantsAndConfigs.gridRhythm) * constantsAndConfigs.gridRhythm |
|||
positionInBlockBackendTicks = sp - blockStartOffset |
|||
api.addGraphItem(blockId, positionInBlockBackendTicks, self.getYAsBackendValue(y)) |
|||
|
|||
class CCGraphTransparentBlock(QtWidgets.QGraphicsRectItem): |
|||
instances = [] |
|||
|
|||
def __init__(self, parent, staticExportItem, x, y, w, h): |
|||
self.__class__.instances.append(self) |
|||
super().__init__(x, y, w, h) |
|||
#self.setFlags(QtWidgets.QGraphicsItem.ItemDoesntPropagateOpacityToChildren|QtWidgets.QGraphicsItem.ItemIsMovable) #no mouseReleaseEvent without selection or movable. |
|||
self.setFlags(QtWidgets.QGraphicsItem.ItemDoesntPropagateOpacityToChildren) #no mouseReleaseEvent without selection or movable. |
|||
self.parent = parent # CCPath instance |
|||
self.color = stringToColor(staticExportItem["name"]) |
|||
self.setBrush(self.color) |
|||
self.trans = QtGui.QColor("transparent") |
|||
self.setPen(self.trans) |
|||
self.setBrush(self.trans) |
|||
self.setOpacity(0.2) #mimic background behaviour |
|||
self.staticExportItem = staticExportItem |
|||
|
|||
self.blockEndMarker = CCGraphBlockEndMarker(self, staticExportItem) |
|||
self.blockEndMarker.setParentItem(self) |
|||
self.blockEndMarker.setPos(staticExportItem["duration"] / constantsAndConfigs.ticksToPixelRatio, 0) |
|||
|
|||
self.posBeforeMove = None |
|||
self.cursorPosOnMoveStart = None |
|||
|
|||
def stretchXCoordinates(self, factor): |
|||
"""Reposition the items on the X axis. |
|||
Call goes through all parents/children, starting from ScoreView._stretchXCoordinates. |
|||
Docstring there.""" |
|||
stretchRect(self, factor) |
|||
self.blockEndMarker.setX(self.blockEndMarker.pos().x() * factor) |
|||
|
|||
def mousePressEventCustom(self, event): |
|||
"""Custom gets called by the scene mouse press event directly only when the right keys |
|||
are hold down. |
|||
e.g. shift + middle mouse button for moving this block""" |
|||
self.posBeforeMove = self.pos() |
|||
self.cursorPosOnMoveStart = QtGui.QCursor.pos() |
|||
self.setBrush(self.color) |
|||
super().mousePressEvent(event) |
|||
|
|||
def mouseMoveEventCustom(self, event): |
|||
"""Custom gets called by the scene mouse press event directly only when the right keys |
|||
are hold down |
|||
e.g. shift + middle mouse button for moving this block""" |
|||
# All the positions below don't work. They work fine when dragging Tracks around but not this Item. I can't be bothered to figure out why. |
|||
#scenePos() results ins an item position that is translated down and right. The higher the x/y value the more the offset |
|||
#Instead we calculate our delta ourselves. |
|||
|
|||
#self.setPos(self.mapToItem(self, event.scenePos())) |
|||
#self.setPos(self.mapFromScene(event.scenePos())) |
|||
#posGlobal = QtGui.QCursor.pos() |
|||
#posView = self.parent.parentScore.parentView.mapFromGlobal(posGlobal) #a widget |
|||
#posScene = self.parent.parentScore.parentView.mapToScene(posView) |
|||
#print (posGlobal, posView, posScene, event.scenePos()) |
|||
|
|||
|
|||
if self.cursorPosOnMoveStart: |
|||
self.setPos(event.scenePos()) |
|||
|
|||
""" |
|||
#does not work with zooming |
|||
if self.cursorPosOnMoveStart: |
|||
delta = QtGui.QCursor.pos() - self.cursorPosOnMoveStart |
|||
new = self.posBeforeMove + delta |
|||
if new.x() < 0: |
|||
#self.setPos(0, self.posBeforeMove.y()) |
|||
self.setPos(0, new.y()) |
|||
else: |
|||
#self.setPos(new.x(), self.posBeforeMove.y()) #only move in one track |
|||
self.setPos(new.x(), new.y()) |
|||
#event.ignore() #this blocks the qt movable object since we already move the object on our own. |
|||
""" |
|||
super().mouseMoveEvent(event) |
|||
|
|||
|
|||
def mouseReleaseEventCustom(self, event): |
|||
"""Custom gets called by the scene mouse press event directly only when the right keys |
|||
are hold down""" |
|||
self.setBrush(self.trans) |
|||
self.setPos(self.posBeforeMove) #In case the block was moved to a position where no track is (below the tracks) we just reset the graphics. If the moving was correct then the new position will be set by redrawing the whole Conductor. |
|||
self.posBeforeMove = None |
|||
self.cursorPosOnMoveStart = None |
|||
super().mouseReleaseEvent(event) |
|||
|
|||
def splitHere(self, event): |
|||
posRelativeToBlockStart = event.scenePos().x() * constantsAndConfigs.ticksToPixelRatio - self.x() * constantsAndConfigs.ticksToPixelRatio |
|||
if constantsAndConfigs.snapToGrid: |
|||
posRelativeToBlockStart = round(posRelativeToBlockStart / constantsAndConfigs.gridRhythm) * constantsAndConfigs.gridRhythm |
|||
|
|||
if posRelativeToBlockStart > 0: |
|||
api.splitCCBlock(self.staticExportItem["id"], int(posRelativeToBlockStart)) |
|||
|
|||
def contextMenuEvent(self, event): |
|||
listOfLabelsAndFunctions = [ |
|||
("split here", lambda: self.splitHere(event)), |
|||
("duplicate", lambda: api.duplicateCCBlock(self.staticExportItem["id"])), |
|||
("create content link", lambda: api.duplicateContentLinkCCBlock(self.staticExportItem["id"])), |
|||
("unlink", lambda: api.unlinkCCBlock(self.staticExportItem["id"])), |
|||
("separator", None), |
|||
("join with next block", lambda: api.mergeWithNextGraphBlock(self.staticExportItem["id"])), |
|||
("delete block", lambda: api.deleteCCBlock(self.staticExportItem["id"])), |
|||
("separator", None), |
|||
("append block at the end", lambda ccNum = self.parent.getCCNumber(): api.appendGraphBlock(self.parent.parentDataTrackId, ccNum)), |
|||
] |
|||
callContextMenu(listOfLabelsAndFunctions) |
|||
|
|||
class CCGraphBlockEndMarker(QtWidgets.QGraphicsLineItem): |
|||
def __init__(self, parentTransparentBlock, staticExportItem): |
|||
super(CCGraphBlockEndMarker, self).__init__(0,-28,0,28) #x, y, w, h |
|||
self.parentTransparentBlock = parentTransparentBlock |
|||
self.parentCCPath = self.parentTransparentBlock.parent |
|||
self.staticExportItem = staticExportItem |
|||
self.setFlags(QtWidgets.QGraphicsItem.ItemIsMovable|QtWidgets.QGraphicsItem.ItemSendsGeometryChanges|QtWidgets.QGraphicsItem.ItemIsFocusable) |
|||
self.setAcceptHoverEvents(True) |
|||
self.setCursor(QtCore.Qt.SizeHorCursor) |
|||
self.lastPos = self.pos() |
|||
#self.setBrush(QtGui.QColor("red")) |
|||
pen = QtGui.QPen() # creates a default pen |
|||
if not self.staticExportItem["exportsAllItems"]: |
|||
pen.setStyle(QtCore.Qt.DashDotLine) |
|||
pen.setWidth(2) |
|||
self.setPen(pen) |
|||
self.setZValue(10) |
|||
|
|||
self.inactivePen = pen |
|||
self.inactivePen.setColor(QtGui.QColor("black")) |
|||
|
|||
self.activePen = QtGui.QPen(pen) |
|||
self.activePen.setColor(QtGui.QColor("cyan")) |
|||
|
|||
def allItemsRightOfMe(self): |
|||
for item in self.parentCCPath.items: |
|||
if item.x() > self.x(): |
|||
yield item |
|||
|
|||
def shape(self): |
|||
"""Qt Function |
|||
Return a more accurate shape for this item so that |
|||
mouse hovering is more accurate""" |
|||
path = QtGui.QPainterPath() |
|||
path.addRect(QtCore.QRectF(-3, -1/2 * constantsAndConfigs.trackHeight, 9, constantsAndConfigs.trackHeight)) #this is directly related to inits parameter x, y, w, h |
|||
return path |
|||
|
|||
def mouseReleaseEvent(self, event): |
|||
"""After moving a point around |
|||
send an update to the backend""" |
|||
super(CCGraphBlockEndMarker, self).mouseReleaseEvent(event) |
|||
if event.button() == 1: #QtCore.Qt.MouseButton.LeftButton |
|||
x = event.scenePos().x() |
|||
#x = self.x() |
|||
x = x * constantsAndConfigs.ticksToPixelRatio |
|||
endingRelativeToBlockStart = x - self.staticExportItem["position"] |
|||
if constantsAndConfigs.snapToGrid: |
|||
endingRelativeToBlockStart = round(endingRelativeToBlockStart / constantsAndConfigs.gridRhythm) * constantsAndConfigs.gridRhythm |
|||
assert endingRelativeToBlockStart > 0 |
|||
api.changeGraphBlockDuration(self.staticExportItem["id"], endingRelativeToBlockStart) |
|||
|
|||
|
|||
def mousePressEvent(self, event): |
|||
super(CCGraphBlockEndMarker, self).mousePressEvent(event) |
|||
|
|||
if event.button() == 1: #QtCore.Qt.MouseButton.LeftButton |
|||
for i in self.allItemsRightOfMe(): |
|||
i.hide() |
|||
|
|||
def mouseMoveEvent(self, event): |
|||
"""Only active when the item is also selected and left |
|||
mouse button is down. Not any mouse event, no hover.""" |
|||
super(CCGraphBlockEndMarker, self).mouseMoveEvent(event) |
|||
self.setY(self.lastPos.y()) |
|||
|
|||
def hoverEnterEvent(self, event): |
|||
#self.setZValue(20) |
|||
#self.parentTransparentBlock.setZValue(19) |
|||
self.setPen(self.activePen) |
|||
self.parentTransparentBlock.setBrush(self.parentTransparentBlock.color) |
|||
|
|||
def hoverLeaveEvent(self, event): |
|||
#self.setZValue(10) |
|||
#self.parentTransparentBlock.setZValue(9) |
|||
self.setPen(self.inactivePen) |
|||
self.parentTransparentBlock.setBrush(self.parentTransparentBlock.trans) |
|||
|
|||
|
|||
class CCInterpolatedPoint(QtWidgets.QGraphicsEllipseItem): |
|||
"""This is practically read-only""" |
|||
def __init__(self): |
|||
super(CCInterpolatedPoint, self).__init__(0, 0,1,1) #x,y,w,h |
|||
self.setOpacity(0.6) |
|||
self.setBrush(QtCore.Qt.black) #fill |
|||
self.setEnabled(False) |
|||
self.setZValue(1) |
|||
|
|||
class CCUserPoint(QtWidgets.QGraphicsEllipseItem): |
|||
"""the position is set by the parent""" |
|||
def __init__(self, parentCCPath, staticExportItem): |
|||
super(CCUserPoint, self).__init__(-3,-3,6,6) #x,y,w,h |
|||
#color = QtGui.QColor() |
|||
#color.setHsl(127-abs(value), 255, 127) #l(h, s, l) # 8bit values. Sets a HSL color value; h is the hue, s is the saturation, l is the lightness. l goes from black(0), over color(255/2) to white (255). #0 hue is green, 128 is red |
|||
#self.setBrush(color) #fill |
|||
self.setBrush(QtGui.QColor("cyan")) |
|||
self.setFlags(QtWidgets.QGraphicsItem.ItemIsMovable|QtWidgets.QGraphicsItem.ItemSendsGeometryChanges|QtWidgets.QGraphicsItem.ItemIsFocusable) |
|||
self.setAcceptHoverEvents(True) |
|||
self.setCursor(QtCore.Qt.SizeAllCursor) |
|||
self.parentCCPath = parentCCPath |
|||
self.staticExportItem = staticExportItem |
|||
self.interpolatedItemsRight = [] |
|||
self.interpolatedItemsLeft = [] |
|||
self.setZValue(9) |
|||
|
|||
def shape(self): |
|||
"""Return a more accurate shape for this item so that |
|||
mouse hovering is more accurate""" |
|||
path = QtGui.QPainterPath() |
|||
path.addEllipse(QtCore.QRectF(-5, -5, 14, 14)) #this is directly related to inits parameter. -3, -3, 6, 6. |
|||
return path |
|||
|
|||
def mousePressEvent(self, event): |
|||
super().mousePressEvent(event) |
|||
self.lastPos = self.pos() |
|||
|
|||
if event.button() == 1: #QtCore.Qt.MouseButton.LeftButton |
|||
self.setCursor(QtCore.Qt.BlankCursor) |
|||
for i in self.interpolatedItemsLeft + self.interpolatedItemsRight: |
|||
i.hide() |
|||
|
|||
def mouseDoubleClickEvent(self, event): |
|||
self.changeCCValue() |
|||
|
|||
def hoverEnterEvent(self, event): |
|||
self.grabKeyboard() |
|||
#self.setFocus() #do NOT set the focus. GrabKeyboard is enough. The focus will stay even after hoverLeaveEvent so that Delete will delete the last hovered item. not good! |
|||
|
|||
def hoverLeaveEvent(self, event): |
|||
"""reverse hoverEnterEvent""" |
|||
self.ungrabKeyboard() |
|||
|
|||
def keyPressEvent(self, event): |
|||
"""Handle the delete item key. |
|||
Needs grabKeyboard, but NOT setFocus""" |
|||
key = event.key() |
|||
if key == 16777223: |
|||
self.delete() |
|||
else: |
|||
return super().keyPressEvent(event) |
|||
|
|||
def mouseReleaseEvent(self, event): |
|||
"""After moving a point around |
|||
send an update to the backend""" |
|||
super(CCUserPoint, self).mouseReleaseEvent(event) |
|||
self.setCursor(QtCore.Qt.SizeAllCursor) |
|||
if event.button() == 1: #QtCore.Qt.MouseButton.LeftButton |
|||
api.changeGraphItem(self.staticExportItem["id"], self.getXDifferenceAsBackendValue(), self.getYAsBackendValue()) #send update to the backend, don't wait for callback. |
|||
|
|||
def mouseMoveEvent(self, event): |
|||
"""Only active when the item is also selected and left |
|||
mouse button is down. Not any mouse event, no hover.""" |
|||
modifiers = QtWidgets.QApplication.keyboardModifiers() |
|||
super(CCUserPoint, self).mouseMoveEvent(event) |
|||
if modifiers == QtCore.Qt.ShiftModifier: |
|||
self.setY(self.lastPos.y()) |
|||
elif modifiers == QtCore.Qt.AltModifier: |
|||
self.setX(self.lastPos.x()) |
|||
|
|||
absoluteXPosition = self.x() * constantsAndConfigs.ticksToPixelRatio |
|||
withinBlock = self.staticExportItem["minPossibleAbsolutePosition"] <= absoluteXPosition <= self.staticExportItem["maxPossibleAbsolutePosition"] |
|||
if not withinBlock: |
|||
if absoluteXPosition < self.staticExportItem["minPossibleAbsolutePosition"]: |
|||
self.setX(self.staticExportItem["minPossibleAbsolutePosition"] / constantsAndConfigs.ticksToPixelRatio) |
|||
else: |
|||
self.setX(self.staticExportItem["maxPossibleAbsolutePosition"] / constantsAndConfigs.ticksToPixelRatio) |
|||
#Make mouse cursor movement not weird. |
|||
a = self.scene().parentView.mapFromScene(self.pos()) |
|||
b = self.scene().parentView.viewport().mapToGlobal(a) |
|||
QtGui.QCursor.setPos(b.x(), QtGui.QCursor.pos().y()) |
|||
|
|||
withinYRange = -28 <= self.y() <= 28 |
|||
if not withinYRange: |
|||
if self.y() < -28: |
|||
self.setY(-27.9) |
|||
else: |
|||
self.setY(28) |
|||
#Make mouse cursor movement not weird. |
|||
a = self.scene().parentView.mapFromScene(self.pos()) |
|||
b = self.scene().parentView.viewport().mapToGlobal(a) |
|||
QtGui.QCursor.setPos(QtGui.QCursor.pos().x(), b.y()) |
|||
|
|||
def delete(self): |
|||
"""Instruct the backend to delete this item. Will trigger a |
|||
callback to redraw the graphTrack""" |
|||
api.removeGraphItem(self.staticExportItem["id"]) |
|||
|
|||
def contextMenuEvent(self, event): |
|||
menu = QtWidgets.QMenu() |
|||
|
|||
listOfLabelsAndFunctions = [ |
|||
("interpolation Type", self.changeInterpolationType), |
|||
("CC Value", self.changeCCValue), |
|||
("delete", self.delete), #deletes the last hover item which is of course the current one. |
|||
] |
|||
|
|||
for text, function in listOfLabelsAndFunctions: |
|||
if text == "separator": |
|||
self.addSeparator() |
|||
else: |
|||
a = QtWidgets.QAction(text, menu) |
|||
menu.addAction(a) |
|||
a.triggered.connect(function) |
|||
|
|||
pos = QtGui.QCursor.pos() |
|||
pos.setY(pos.y() + 5) |
|||
self.ungrabMouse() #if we don't ungrab and the user clicks the context menu "away" by clicking in an empty area this will still get registered as mouseclick belonging to the current item and change the value to some insane amount, breaking the point. |
|||
menu.exec_(pos) |
|||
|
|||
def changeInterpolationType(self): |
|||
graphTypeString = QtWidgets.QInputDialog.getItem(self.scene().parentView, "Interpolation Type", "choose Interpolation Type", api.getListOfGraphInterpolationTypesAsStrings(), 0, False) |
|||
if graphTypeString[1]: #[1] bool if canceled |
|||
api.changeGraphItemInterpolation(self.staticExportItem["id"], graphTypeString[0]) |
|||
|
|||
def changeCCValue(self): |
|||
ccValue = QtWidgets.QInputDialog.getInt(self.scene().parentView, "CC Value", "specify midi Continuous Control value", value=64, minValue=0, maxValue=127) |
|||
if ccValue[1]: #[1] bool if canceled |
|||
api.changeGraphItem(self.staticExportItem["id"], self.getXDifferenceAsBackendValue(), ccValue[0]) #send update to the backend, don't wait for callback. |
|||
|
|||
def getYAsBackendValue(self): |
|||
newCCValue = int(self.y() / 0.4375 - 64) #value goes from -0 to -127 and are compressed to fit between two lines in the staff. |
|||
newCCValue = abs(newCCValue) |
|||
assert 0 <= newCCValue < 128 |
|||
return newCCValue |
|||
|
|||
def getXDifferenceAsBackendValue(self): |
|||
"""The problem is that we need the position relativ to its |
|||
parent block, which we don't use in that way in the GUI. |
|||
So we calculate the difference to the old position""" |
|||
|
|||
oldPositionAbsolute = int(self.staticExportItem["position"]) |
|||
newPositionAbsolute = self.pos().x() * constantsAndConfigs.ticksToPixelRatio |
|||
if constantsAndConfigs.snapToGrid: |
|||
newPositionAbsolute = round(newPositionAbsolute / constantsAndConfigs.gridRhythm) * constantsAndConfigs.gridRhythm |
|||
diff = int(-1 * (oldPositionAbsolute - newPositionAbsolute)) |
|||
return diff |
@ -0,0 +1,920 @@ |
|||
#! /usr/bin/env python3 |
|||
# -*- coding: utf-8 -*- |
|||
""" |
|||
Copyright 2017, Nils Hilbricht, Germany ( https://www.hilbricht.net ) |
|||
|
|||
This file is part of Laborejo ( https://www.laborejo.org ) |
|||
|
|||
Laborejo is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
""" |
|||
|
|||
from PyQt5 import QtCore, QtGui, QtSvg, QtWidgets |
|||
from itertools import groupby |
|||
from engine.items import Chord, KeySignature |
|||
from .constantsAndConfigs import constantsAndConfigs |
|||
from template.qtgui.helper import stretchRect |
|||
import engine.api as api |
|||
|
|||
"""All svg items (execept pitch-related) graphics are expected to be |
|||
already on the correct position and will be inserter ON the middle line |
|||
That means g-clef needs to be 14 pxiels below the y=0 coordinate since |
|||
these are two stafflines. Or in other words: Open the graphic and |
|||
scale and reposition it until it fits correctly when inserted with |
|||
setPos(0,0)""" |
|||
|
|||
#Atomic items: |
|||
|
|||
oneRectToReturnThemAll = QtCore.QRectF(0,0,0,0) #prevent the annoying "NotImplementError" from Qt for boundingRect. For items that don't need any collision detection. |
|||
|
|||
class GuiTieCurveGraphicsItem(QtWidgets.QGraphicsPathItem): |
|||
pen = QtGui.QPen() |
|||
pen.setCapStyle(QtCore.Qt.RoundCap) |
|||
pen.setWidth(2) |
|||
|
|||
def __init__(self, noteExportObject): |
|||
"""The origin of this item is the scene position. |
|||
We create a curve by drawing a straight line from zero |
|||
to end (the local item and the receiving tie item) and then |
|||
bend it down in the middle, leaving one of the control points |
|||
empty (0,0).""" |
|||
super().__init__() |
|||
self.setPen(GuiTieCurveGraphicsItem.pen) |
|||
if noteExportObject["dotOnLine"] < 0: #higher than the middle line |
|||
#self.translate(6, -5) |
|||
pass |
|||
else: |
|||
if noteExportObject["notehead"] <= 1: #whole notes have a bug in the gui because the backend stem info is misleading. Works fine except for ties. |
|||
pass #TODO |
|||
#self.translate(6, 0) |
|||
else: |
|||
pass #TODO |
|||
#self.translate(6, 6) |
|||
|
|||
self.noteExportObject = noteExportObject |
|||
self.draw() |
|||
|
|||
def draw(self): |
|||
lengthInPixel = self.noteExportObject["tieDistanceInTicks"] / constantsAndConfigs.ticksToPixelRatio |
|||
path = QtGui.QPainterPath() |
|||
path.cubicTo(0, 0, lengthInPixel/2, constantsAndConfigs.stafflineGap, lengthInPixel, 0) # ctrlPt1x, ctrlPt1y, ctrlPt2x, ctrlPt2y, endPtx, endPty |
|||
self.setPath(path) #set path edits the old one in place |
|||
|
|||
def stretchXCoordinates(self, factor): |
|||
"""Called directly by the track by iterating through instances""" |
|||
self.draw() |
|||
|
|||
|
|||
class GuiPositionMarker(QtWidgets.QGraphicsRectItem): |
|||
def __init__(self, height, position = 1): |
|||
"""A simple position marker that connects an annotation above a staff with the staff. |
|||
Height and position are calculated in stafflines/gaps""" |
|||
super().__init__(-2, position*constantsAndConfigs.stafflineGap, 1, height * constantsAndConfigs.stafflineGap) #x,y, w, h |
|||
self.setBrush(QtCore.Qt.black) |
|||
|
|||
class GuiTupletNumber(QtWidgets.QGraphicsItem): |
|||
"""The baseline is the scene Position. |
|||
We position in the up direction""" |
|||
|
|||
pen = QtGui.QPen() |
|||
pen.setColor(QtGui.QColor("darkGrey")) |
|||
pen.setWidth(1) |
|||
|
|||
def paint(self, *args): |
|||
pass |
|||
def boundingRect(self, *args): |
|||
return oneRectToReturnThemAll |
|||
|
|||
def __init__(self, upper, lower): |
|||
super().__init__() |
|||
#self.upper = QtWidgets.QGraphicsSimpleTextItem(str(upper)) |
|||
#self.divider = QtWidgets.QGraphicsLineItem(0,0,10,0) |
|||
#self.divider.rotate(-70) |
|||
#self.divider.setPen(GuiTupletNumber.pen) |
|||
self.lower = QtWidgets.QGraphicsSimpleTextItem(str(lower)) |
|||
|
|||
#self.upper.setParentItem(self) |
|||
#self.divider.setParentItem(self) |
|||
self.lower.setParentItem(self) |
|||
|
|||
#self.upper.setPos(-1,-8) |
|||
#self.divider.setPos(5,5) |
|||
#self.lower.setPos(7,-5) |
|||
|
|||
self.lower.setPos(-1,-4) |
|||
self.setScale(0.75) |
|||
|
|||
class GuiItem(QtWidgets.QGraphicsItem): |
|||
"""A blank item. Subclass to implement your own""" |
|||
def __init__(self, staticItem): |
|||
super().__init__() |
|||
self.staticItem = staticItem |
|||
dur = self.staticItem["completeDuration"] |
|||
exceptions = ["BlockEndMarker"] |
|||
#Deactivated after stretchX was reimplemented properly as recursive function and not a catch all on all items. I don't think that was used anywhere else. #self.rectangles = [] #Prevents typechecking. only implemented in GuiChord |
|||
|
|||
self.pixelPosition = self.staticItem["tickindex"] / constantsAndConfigs.ticksToPixelRatio |
|||
self.pixelWidth = self.staticItem["completeDuration"] / constantsAndConfigs.ticksToPixelRatio |
|||
|
|||
#if dur or self.staticItem["type"] in exceptions: |
|||
# self.pixelPosition = self.staticItem["tickindex"] / constantsAndConfigs.ticksToPixelRatio |
|||
# self.pixelWidth = self.staticItem["completeDuration"] / constantsAndConfigs.ticksToPixelRatio |
|||
#else: |
|||
# self.pixelPosition = self.staticItem["tickindex"] / constantsAndConfigs.ticksToPixelRatio + constantsAndConfigs.negativeMagicPixel*6 #an offset to the left. this will still get in the way for short notes, but for now it is better than colliding 100% when placed on the note. #TODO: place above the track |
|||
# self.pixelWidth = 25 #Does not need to be the actual size because this is ONLY important when the item gets inserted at the appending position and the cursor needs to jump "over" the current item, because there is no next one. This is the direct response to the cursor method that sets the appending position as items tickposition+pixelwidth. |
|||
|
|||
def updateVisibility(self): |
|||
"""Prevent against typechecking. This method is only implemented |
|||
in GuiChord""" |
|||
pass |
|||
|
|||
def paint(self, *args): |
|||
pass |
|||
def boundingRect(self, *args): |
|||
return oneRectToReturnThemAll |
|||
|
|||
def stretchXCoordinates(self, factor): |
|||
"""Reposition the items on the X axis. |
|||
Call goes through all parents/children, starting from ScoreView._stretchXCoordinates. |
|||
Docstring there.""" |
|||
pass |
|||
|
|||
class GuiRectangleNotehead(QtWidgets.QGraphicsRectItem): |
|||
"""Displays the pitch and actual duration of one note when in rectangle view mode""" |
|||
|
|||
def __init__(self, parent, noteExportObject): |
|||
#Dimensions and Position |
|||
x = noteExportObject["leftModInTicks"] / constantsAndConfigs.ticksToPixelRatio |
|||
y = -1 * constantsAndConfigs.stafflineGap / 2 + 1 |
|||
w = (noteExportObject["rightModInTicks"] + noteExportObject["completeDuration"] - noteExportObject["leftModInTicks"]) / constantsAndConfigs.ticksToPixelRatio |
|||
h = constantsAndConfigs.stafflineGap #- 2 #2 pixel to make room for the pen-width. |
|||
super().__init__(x, y, w, h) |
|||
self.setPos(0, constantsAndConfigs.stafflineGap * noteExportObject["dotOnLine"] / 2) |
|||
self.setParentItem(parent) |
|||
|
|||
#Prepare self.shape() |
|||
#NOTE: this was from a time when mouse modification was possible. Leave for later use. |
|||
#self.path = QtGui.QPainterPath() |
|||
#self.pathRect = QtCore.QRectF(x, y-1, w, h+4) #this is directly related to inits parameter x, y, w, h |
|||
#self.path.addRect(self.pathRect) |
|||
#self.setCursor(QtCore.Qt.SizeHorCursor) |
|||
|
|||
#Different color when the duration was modified by the user |
|||
inactive = "green" if noteExportObject["manualOverride"] else "black" |
|||
self.inactiveColor = QtGui.QColor(inactive) |
|||
self.setBrush(self.inactiveColor) |
|||
|
|||
#Pen for the borders |
|||
pen = QtGui.QPen() |
|||
pen.setWidth(0) |
|||
pen.setColor(QtGui.QColor("darkGrey")) |
|||
self.setPen(pen) |
|||
|
|||
#Since accidentals are part of the notehead we need one here. It doesn't matter if the traditional notehead already has one |
|||
if noteExportObject["accidental"]: #0 means no difference to keysig |
|||
self.accidental = GuiNote.createAccidentalGraphicsItem(self, noteExportObject["accidental"]) |
|||
self.accidental.setPos(0, 0) #not analogue to the notehead acciental position because here we choose the rectangle as parent |
|||
self.accidental.setParentItem(self) |
|||
|
|||
#NOTE: this was from a time when mouse modification was possible. Leave for later use. |
|||
#def shape(self): |
|||
# """Qt Function |
|||
# Return a more accurate shape for this item so that |
|||
# mouse hovering is more accurate. |
|||
# |
|||
# Must be within the bounding rect.""" |
|||
# return self.path |
|||
|
|||
#def boundingRect(self, *args): |
|||
# """Keep in syn with self.shape()""" |
|||
# return self.pathRect |
|||
|
|||
#def paint(self, *args): |
|||
#Do NOT implement this. This actually works already. The parent item needs this. |
|||
|
|||
|
|||
class GuiRectangleVelocity(QtWidgets.QGraphicsRectItem): |
|||
"""Displays the velocity of one note when in rectangle view mode. |
|||
|
|||
The Y-position is fixed on the staff and not bound to the rectangle notehead. |
|||
This makes it possible for the user to compare velocities visually. |
|||
|
|||
""" |
|||
def __init__(self, parent, noteExportObject): |
|||
#Dimensions and Position |
|||
x = noteExportObject["leftModInTicks"] / constantsAndConfigs.ticksToPixelRatio #same as GuiRectangleNotehead |
|||
y = -1 * (noteExportObject["velocity"] / constantsAndConfigs.velocityToPixelRatio) |
|||
w = (noteExportObject["rightModInTicks"] + noteExportObject["completeDuration"] - noteExportObject["leftModInTicks"]) / constantsAndConfigs.ticksToPixelRatio #same as GuiRectangleNotehead |
|||
h = noteExportObject["velocity"] / constantsAndConfigs.velocityToPixelRatio #velocityToPixelRation has its own compress factor since values can go from 0 to 127 |
|||
super().__init__(x, y, w, h) |
|||
self.setPos(0, 2*constantsAndConfigs.stafflineGap) #The position is fixed to the staff, not to the GuiRectangleNotehead. |
|||
self.setParentItem(parent) |
|||
|
|||
#Prepare self.shape() |
|||
#NOTE: this was from a time when mouse modification was possible. Leave for later use. |
|||
#self.path = QtGui.QPainterPath() |
|||
#self.pathRect = QtCore.QRectF(x, y-1, w, h+4) #this is directly related to inits parameter x, y, w, h |
|||
#self.path.addRect(self.pathRect) |
|||
#self.setCursor(QtCore.Qt.SizeHorCursor) |
|||
|
|||
#Colors and Apperance |
|||
inactive = "green" if noteExportObject["velocityManualOverride"] else "lightGray" |
|||
self.inactiveVelocityColor = QtGui.QColor(inactive) |
|||
self.activeVelocityColor = QtGui.QColor("darkCyan") |
|||
self.setBrush(self.inactiveVelocityColor) |
|||
self.setOpacity(0.3) |
|||
|
|||
#Pen for the borders |
|||
#The pen interferes with the precise appearance we want for live notes |
|||
pen = QtGui.QPen() |
|||
pen.setCapStyle(QtCore.Qt.RoundCap) |
|||
pen.setJoinStyle(QtCore.Qt.RoundJoin) |
|||
pen.setWidth(2) |
|||
pen.setColor(QtGui.QColor("black")) |
|||
self.setPen(pen) |
|||
|
|||
#Attach a label that displays the velocity as midi value 0-127 |
|||
#label = QtWidgets.QGraphicsSimpleTextItem(str(noteExportObject["velocity"])) |
|||
#label.setPos(3, -3) |
|||
#label.setParentItem(parent) |
|||
|
|||
#Misc Qt Flags |
|||
self.setAcceptHoverEvents(False) #Make this explicit so it will be remembered that the gui rectangle velocity is a display only. |
|||
|
|||
#NOTE: this was from a time when mouse modification was possible. Leave for later use. |
|||
#def shape(self): |
|||
# """Qt Function |
|||
# Return a more accurate shape for this item so that |
|||
# mouse hovering is more accurate. |
|||
# |
|||
# Must be within the bounding rect.""" |
|||
# return self.path |
|||
|
|||
#def boundingRect(self, *args): |
|||
# """Keep in syn with self.shape()""" |
|||
# return self.pathRect |
|||
|
|||
#def paint(self, *args): |
|||
#Do NOT implement this. This actually works already. The parent item needs this. |
|||
|
|||
class GuiRectangle(QtWidgets.QGraphicsItem): |
|||
"""An alternative notehead, used to change velocity and duration |
|||
finetuning. |
|||
|
|||
This is a GraphicsItem with two main components: |
|||
The notehead which displays the duration and pitch of the note |
|||
The Velocity which displays only the velocity. |
|||
Both components share their Y position in the staff so they are one item. |
|||
|
|||
The notehead rectangles have full opacity and can't layer. |
|||
The velocity rectangles layer and only a grahpical highlight shows the active one. |
|||
""" |
|||
|
|||
def __init__(self, noteExportObject): |
|||
super().__init__() |
|||
self.notehead = GuiRectangleNotehead(parent = self, noteExportObject = noteExportObject) |
|||
self.velocity = GuiRectangleVelocity(parent = self, noteExportObject = noteExportObject) |
|||
self.notehead.setZValue(10) #within GuiRectangle |
|||
|
|||
def boundingRect(self, *args): |
|||
return oneRectToReturnThemAll |
|||
def paint(self, *args): |
|||
pass |
|||
|
|||
class GuiNote(QtWidgets.QGraphicsItem): |
|||
def __init__(self, noteExportObject, directionRightAndUpwards): |
|||
super(GuiNote, self).__init__() |
|||
self.createNote(noteExportObject, directionRightAndUpwards) |
|||
|
|||
def paint(self, *args): |
|||
pass |
|||
def boundingRect(self, *args): |
|||
return oneRectToReturnThemAll |
|||
|
|||
def createNote(self, noteExportObject, directionRightAndUpwards): |
|||
#note head |
|||
self.noteExportObject = noteExportObject |
|||
#self.noteHead = GuiNote.noteheads[noteExportObject["notehead"]]() |
|||
self.noteHead = self.createNoteheadGraphicsItem(noteExportObject["notehead"]) |
|||
self.noteHead.setPos(0, constantsAndConfigs.stafflineGap * noteExportObject["dotOnLine"] / 2) |
|||
self.noteHead.setParentItem(self) |
|||
#Ledger lines need to be done in the chord to not be redundant and work for multiple notes. |
|||
if noteExportObject["accidental"]: #0 means no difference to keysig |
|||
self.accidental = self.createAccidentalGraphicsItem(noteExportObject["accidental"]) |
|||
self.accidental.setPos(0, constantsAndConfigs.stafflineGap * noteExportObject["dotOnLine"] / 2) |
|||
self.accidental.setParentItem(self) |
|||
|
|||
for dot in range(noteExportObject["dots"]): |
|||
d = QtSvg.QGraphicsSvgItem(":svg/dot.svg") |
|||
d.setPos((dot+1)*4+6, constantsAndConfigs.stafflineGap * noteExportObject["dotOnLine"] / 2) |
|||
d.setParentItem(self) |
|||
|
|||
for i, (upper, lower) in enumerate(noteExportObject["tuplets"]): |
|||
tuplet = GuiTupletNumber(upper, lower) |
|||
|
|||
if directionRightAndUpwards: |
|||
tuplet.setPos(3, -6*(i+1)) |
|||
else: |
|||
tuplet.setPos(3, 6*(i+1)) |
|||
|
|||
tuplet.setParentItem(self.noteHead) |
|||
|
|||
if noteExportObject["durationKeyword"]: |
|||
self.durationKeywordGlyph = GuiNote.durationKeywords[noteExportObject["durationKeyword"]](noteExportObject) |
|||
if directionRightAndUpwards: |
|||
self.durationKeywordGlyph.setPos(3, 6) |
|||
else: |
|||
self.durationKeywordGlyph.setPos(3, -6) |
|||
|
|||
self.durationKeywordGlyph.setParentItem(self.noteHead) |
|||
|
|||
|
|||
def createNoteheadGraphicsItem(self, number): |
|||
item = QtSvg.QGraphicsSvgItem() |
|||
item.setSharedRenderer(GuiNote.noteheads[number]) |
|||
return item |
|||
|
|||
def createAccidentalGraphicsItem(self, number): |
|||
item = QtSvg.QGraphicsSvgItem() |
|||
item.setSharedRenderer(GuiNote.accidentals[number]) |
|||
return item |
|||
|
|||
durationKeywords = { #0 is default and has no markers. |
|||
api.D_STACCATO : lambda noteExportObject: QtSvg.QGraphicsSvgItem(":svg/scriptsStaccato.svg"), |
|||
api.D_TENUTO : lambda noteExportObject: QtWidgets.QGraphicsSimpleTextItem("-"), #TODO: tenuto glyph |
|||
api.D_TIE : lambda noteExportObject: GuiTieCurveGraphicsItem(noteExportObject), |
|||
} |
|||
|
|||
noteheads = { |
|||
4 : QtSvg.QSvgRenderer(":svg/noteheadsBlack.svg"), #and 8, 16, 32, ... |
|||
2 : QtSvg.QSvgRenderer(":svg/noteheadsHalf.svg"), |
|||
1 : QtSvg.QSvgRenderer(":svg/noteheadsWhole.svg"), |
|||
0 : QtSvg.QSvgRenderer(":svg/noteheadsBrevis.svg"), |
|||
-1 : QtSvg.QSvgRenderer(":svg/noteheadsLonga.svg"), |
|||
-2 : QtSvg.QSvgRenderer(":svg/noteheadsMaxima.svg"), |
|||
} |
|||
|
|||
#Accidental .svg are shifted to the left a bit already so we can place them on the same x-coordinates as the notes. |
|||
accidentals = { |
|||
1 : QtSvg.QSvgRenderer(":svg/accidentalsNatural.svg"), |
|||
10 : QtSvg.QSvgRenderer(":svg/accidentalsSharp.svg"), |
|||
20 : QtSvg.QSvgRenderer(":svg/accidentalsDoublesharp.svg"), |
|||
-10 : QtSvg.QSvgRenderer(":svg/accidentalsFlat.svg"), |
|||
-20 : QtSvg.QSvgRenderer(":svg/accidentalsFlatFlat.svg"), |
|||
} |
|||
|
|||
|
|||
def stretchXCoordinates(self, factor): |
|||
"""Reposition the items on the X axis. |
|||
Call goes through all parents/children, starting from ScoreView._stretchXCoordinates. |
|||
Docstring there.""" |
|||
try: |
|||
self.durationKeywordGlyph.stretchXCoordinates(factor) |
|||
except AttributeError: #does not have any attachements or the attachment does not have the stretch function (e.g. staccato) |
|||
pass |
|||
|
|||
#Top level items, all based on GuiItem. |
|||
|
|||
class GuiChord(GuiItem): |
|||
"""staticItem is a chord Dict which has another list of dicts: |
|||
staticItem["notelist"]. |
|||
Each note with its notehead, accidental etc is in there. |
|||
This means that a chord can consist of multiple durations as well |
|||
as pitches, of course. |
|||
|
|||
For spacing calculations the chord itself gives us a duration as |
|||
well.""" |
|||
|
|||
def __init__(self, staticItem,): |
|||
super().__init__(staticItem) |
|||
self.staticItem = staticItem |
|||
self.rectangles = [] |
|||
self.notes = [] |
|||
self.stem = None #stored for rectangle/head switching |
|||
self.flag = None #stored for rectangle/head switching |
|||
self.beamGroupGlyph = None #stored for rectangle/head switching |
|||
#The actual beams are in the track |
|||
|
|||
self.createRectanglesFromData() |
|||
self.createNotesFromData() |
|||
self.createGenericFromData() |
|||
self.updateVisibility() |
|||
|
|||
|
|||
ledgerPen = QtGui.QPen(QtCore.Qt.SolidLine) |
|||
ledgerPen.setCapStyle(QtCore.Qt.RoundCap) |
|||
ledgerPen.setWidth(1) |
|||
|
|||
stemPen = QtGui.QPen(QtCore.Qt.SolidLine) |
|||
stemPen.setCapStyle(QtCore.Qt.RoundCap) |
|||
stemPen.setWidth(1.5) |
|||
|
|||
flags = { #QGraphicsItems can only be used once so we have to save a class constructor here instead of an instance. |
|||
1024 : lambda: QtSvg.QGraphicsSvgItem(":svg/flag1024.svg"), #TODO |
|||
512 : lambda: QtSvg.QGraphicsSvgItem(":svg/flag512.svg"), #TODO |
|||
256 : lambda: QtSvg.QGraphicsSvgItem(":svg/flag256.svg"), |
|||
128 : lambda: QtSvg.QGraphicsSvgItem(":svg/flag128.svg"), |
|||
64 : lambda: QtSvg.QGraphicsSvgItem(":svg/flag64.svg"), |
|||
32 : lambda: QtSvg.QGraphicsSvgItem(":svg/flag32.svg"), |
|||
16 : lambda: QtSvg.QGraphicsSvgItem(":svg/flag16.svg"), |
|||
8 : lambda: QtSvg.QGraphicsSvgItem(":svg/flag8.svg"), |
|||
-1024 : lambda: QtSvg.QGraphicsSvgItem(":svg/flag1024i.svg"), #TODO |
|||
-512 : lambda: QtSvg.QGraphicsSvgItem(":svg/flag512i.svg"), #TODO |
|||
-256 : lambda: QtSvg.QGraphicsSvgItem(":svg/flag256i.svg"), |
|||
-128 : lambda: QtSvg.QGraphicsSvgItem(":svg/flag128i.svg"), |
|||
-64 : lambda: QtSvg.QGraphicsSvgItem(":svg/flag64i.svg"), |
|||
-32 : lambda: QtSvg.QGraphicsSvgItem(":svg/flag32i.svg"), |
|||
-16 : lambda: QtSvg.QGraphicsSvgItem(":svg/flag16i.svg"), |
|||
-8 : lambda: QtSvg.QGraphicsSvgItem(":svg/flag8i.svg"), |
|||
} |
|||
|
|||
|
|||
def createGenericFromData(self): |
|||
"""Graphics for both rectangles and noteheads""" |
|||
|
|||
#Ledger Lines |
|||
below, above = self.staticItem["ledgerLines"] |
|||
|
|||
for i in range(below): |
|||
line = QtWidgets.QGraphicsLineItem(QtCore.QLineF(0, 0, 12, 0)) |
|||
line.setPen(GuiChord.ledgerPen) |
|||
line.setParentItem(self) |
|||
line.setPos(constantsAndConfigs.negativeMagicPixel, i * constantsAndConfigs.stafflineGap + constantsAndConfigs.stafflineGap*3) |
|||
|
|||
for i in range(above): |
|||
line = QtWidgets.QGraphicsLineItem(QtCore.QLineF(0, 0, 12, 0)) |
|||
line.setPen(GuiChord.ledgerPen) |
|||
line.setParentItem(self) |
|||
line.setPos(constantsAndConfigs.negativeMagicPixel, -1 * i * constantsAndConfigs.stafflineGap - constantsAndConfigs.stafflineGap*3) |
|||
|
|||
#Display relative channel changes |
|||
if not self.staticItem["midiChannelOffset"] == 0: |
|||
channelGlyph = QtWidgets.QGraphicsSimpleTextItem("ch{0:+}".format(self.staticItem["midiChannelOffset"])) |
|||
channelGlyph.setScale(0.75) |
|||
channelGlyph.setParentItem(self) |
|||
channelGlyph.setPos(0, -5*constantsAndConfigs.stafflineGap) |
|||
|
|||
def createRectanglesFromData(self): |
|||
"""Graphics only for rectangles""" |
|||
for noteExportObject in self.staticItem["notelist"]: |
|||
if not noteExportObject["tie"] == "notFirst": |
|||
#Tied notes, except the first, do not produce rectangles because the first note gets an extra large rectangle. |
|||
rectangle = GuiRectangle(noteExportObject) |
|||
self.rectangles.append(rectangle) |
|||
rectangle.setParentItem(self) |
|||
|
|||
def createNotesFromData(self): |
|||
"""Graphics only for noteheads""" |
|||
if self.staticItem["beam"]: |
|||
stemOrBeam = self.staticItem["beam"] |
|||
else: |
|||
stemOrBeam = self.staticItem["stem"] |
|||
|
|||
if stemOrBeam and stemOrBeam[2] > 0: |
|||
self.directionRightAndUpwards = False |
|||
else: |
|||
self.directionRightAndUpwards = True |
|||
|
|||
#Stem - Both for groups and standalone. |
|||
if stemOrBeam: #may be an empty tuple for whole notes and brevis |
|||
line = QtWidgets.QGraphicsLineItem(QtCore.QLineF(0, constantsAndConfigs.stafflineGap * stemOrBeam[1]/2, 0, 0)) #x1, y1, x2, y2 |
|||
line.setPen(GuiChord.stemPen) |
|||
self.stem = line #store as persistent item. Otherwise qt will delete it. |
|||
line.setParentItem(self) |
|||
line.setPos(constantsAndConfigs.magicPixel + stemOrBeam[2]*3, constantsAndConfigs.stafflineGap * stemOrBeam[0] / 2) #stem[2] is left/-1 or right/1 shifting. #4 is the middle |
|||
|
|||
#Flags |
|||
if not self.staticItem["beam"] and self.staticItem["flag"]: |
|||
assert self.stem |
|||
flag = GuiChord.flags[self.staticItem["flag"]]() #the graphic. |
|||
flag.setParentItem(self) |
|||
self.flag = flag #store as persistent item. Otherwise qt will delete it. |
|||
flag.setPos(self.stem.pos().x(), self.stem.line().p1().y() + self.stem.pos().y()) #we already know where the stem-line is. |
|||
|
|||
#Check if this item is the start or end of a beam group and mark it with lilypond syntax |
|||
if self.staticItem["beamGroup"]: |
|||
if self.staticItem["beamGroup"] == "open": |
|||
beamGroupGlyph = QtWidgets.QGraphicsSimpleTextItem("[") |
|||
beamGroupGlyph.setParentItem(self) |
|||
beamGroupGlyph.setPos(0, -5*constantsAndConfigs.stafflineGap) |
|||
elif self.staticItem["beamGroup"] == "close": |
|||
beamGroupGlyph = QtWidgets.QGraphicsSimpleTextItem("]") |
|||
beamGroupGlyph.setParentItem(self) |
|||
beamGroupGlyph.setPos(constantsAndConfigs.magicPixel, -5*constantsAndConfigs.stafflineGap) |
|||
self.beamGroupGlyph = beamGroupGlyph |
|||
|
|||
#Actual Noteheads |
|||
lastDotOnLine = float("inf") |
|||
for noteExportObject in self.staticItem["notelist"]: |
|||
#notes in a chord come in the order highest to lowest |
|||
#determine if we have two neighbouring noteheads. If yes we shift one of the heads to the right |
|||
assert lastDotOnLine > noteExportObject["dotOnLine"] |
|||
if lastDotOnLine - noteExportObject["dotOnLine"] == 1: |
|||
moveThisNoteheadToTheRight = True |
|||
lastDotOnLine = noteExportObject["dotOnLine"] |
|||
lastDotOnLine += 1 #we shift the current notehead. The next pair would be no problem, even if neighbours. |
|||
else: |
|||
moveThisNoteheadToTheRight = False |
|||
lastDotOnLine = noteExportObject["dotOnLine"] |
|||
|
|||
note = GuiNote(noteExportObject, self.directionRightAndUpwards) |
|||
self.notes.append(note) #we need them later for switching between rectangles and noteheads |
|||
note.setParentItem(self) |
|||
if moveThisNoteheadToTheRight: |
|||
note.setX(note.pos().x() + 6) #6 pixels. |
|||
|
|||
|
|||
def stretchXCoordinates(self, factor): |
|||
"""Reposition the items on the X axis. |
|||
Call goes through all parents/children, starting from ScoreView._stretchXCoordinates. |
|||
Docstring there.""" |
|||
for rectNote in self.rectangles: |
|||
stretchRect(rectNote.notehead, factor) |
|||
stretchRect(rectNote.velocity, factor) |
|||
for note in self.notes: #this is for ties only at the moment, the rest does not stretch |
|||
note.stretchXCoordinates(factor) |
|||
|
|||
def updateVisibility(self): |
|||
"""decide during creation what part is visible at the moment. aka. are we in notehead |
|||
or rectangle mode. |
|||
Beams visibility is done in the track function createBeams""" |
|||
|
|||
if constantsAndConfigs.noteHeadMode: |
|||
for r in self.rectangles: |
|||
r.hide() |
|||
for n in self.notes: |
|||
n.show() |
|||
if self.flag: |
|||
self.flag.show() |
|||
if self.stem: |
|||
self.stem.show() |
|||
if self.beamGroupGlyph: |
|||
#This is about the start and end marker [ ] .The beam itself is in the track |
|||
self.beamGroupGlyph.show() |
|||
else: #rectangle mode. |
|||
for r in self.rectangles: |
|||
r.show() |
|||
for n in self.notes: |
|||
n.hide() |
|||
if self.flag: |
|||
self.flag.hide() |
|||
if self.stem: |
|||
self.stem.hide() |
|||
if self.beamGroupGlyph: |
|||
#This is about the start and end marker [ ] .The beam itself is in the track |
|||
self.beamGroupGlyph.hide() |
|||
|
|||
class GuiLiveNote(QtWidgets.QGraphicsRectItem): |
|||
"""A pure rectangle note, meant for live recording.""" |
|||
#TODO: Velocity indicator? |
|||
|
|||
instances = [] |
|||
|
|||
def __init__(self, parent, liveNoteData): |
|||
self.__class__.instances.append(self) |
|||
self.liveNoteData = liveNoteData |
|||
#Dimensions and Position |
|||
x = 0 #A live note is always literally where it sounds |
|||
y = -1 * constantsAndConfigs.stafflineGap / 2 |
|||
w = liveNoteData["duration"] / constantsAndConfigs.ticksToPixelRatio |
|||
h = constantsAndConfigs.stafflineGap |
|||
super().__init__(x, y, w, h) |
|||
self.setY(constantsAndConfigs.stafflineGap * liveNoteData["dotOnLine"] / 2) |
|||
self.setParentItem(parent) |
|||
|
|||
self.setBrush(QtGui.QColor("red")) |
|||
|
|||
#Pen for the borders |
|||
pen = QtGui.QPen() |
|||
pen.setWidth(0) |
|||
self.setPen(pen) |
|||
|
|||
if liveNoteData["accidental"]: #0 means no difference to keysig |
|||
self.accidental = GuiNote.createAccidentalGraphicsItem(self, liveNoteData["accidental"]) |
|||
self.accidental.setPos(0, 0) #not analogue to the notehead acciental position because here we choose the rectangle as parent |
|||
self.accidental.setParentItem(self) |
|||
|
|||
def update(self, duration): |
|||
r = self.rect() |
|||
r.setRight(duration / constantsAndConfigs.ticksToPixelRatio) |
|||
self.setRect(r) |
|||
|
|||
class GuiRest(GuiItem): |
|||
def __init__(self, staticItem): |
|||
super(GuiRest, self).__init__(staticItem) |
|||
self.createGraphicItemsFromData() |
|||
|
|||
rests = { #QGraphicsItems can only be used once so we have to save a class constructor here instead of an instance. |
|||
-1 : lambda: QtSvg.QGraphicsSvgItem(":svg/restLonga.svg"), |
|||
-2 : lambda: QtSvg.QGraphicsSvgItem(":svg/restMaxima.svg"), |
|||
0 : lambda: QtSvg.QGraphicsSvgItem(":svg/restBrevis.svg"), |
|||
1 : lambda: QtSvg.QGraphicsSvgItem(":svg/rest1.svg"), |
|||
2 : lambda: QtSvg.QGraphicsSvgItem(":svg/rest2.svg"), |
|||
4 : lambda: QtSvg.QGraphicsSvgItem(":svg/rest4.svg"), |
|||
8 : lambda: QtSvg.QGraphicsSvgItem(":svg/rest8.svg"), |
|||
16 : lambda: QtSvg.QGraphicsSvgItem(":svg/rest16.svg"), |
|||
32 : lambda: QtSvg.QGraphicsSvgItem(":svg/rest32.svg"), |
|||
64 : lambda: QtSvg.QGraphicsSvgItem(":svg/rest64.svg"), |
|||
128 : lambda: QtSvg.QGraphicsSvgItem(":svg/rest128.svg"), |
|||
256 : lambda: QtSvg.QGraphicsSvgItem(":svg/rest256.svg"), #TODO |
|||
512 : lambda: QtSvg.QGraphicsSvgItem(":svg/rest512.svg"), #TODO |
|||
1024 : lambda: QtSvg.QGraphicsSvgItem(":svg/rest1024.svg"), #TODO |
|||
} |
|||
|
|||
def createGraphicItemsFromData(self): |
|||
self.glyph = GuiRest.rests[self.staticItem["rest"]]() |
|||
self.glyph.setParentItem(self) |
|||
|
|||
for dot in range(self.staticItem["dots"]): |
|||
d = QtSvg.QGraphicsSvgItem(":svg/dot.svg") |
|||
d.setPos((dot+1)*4+6, -2) |
|||
d.setParentItem(self) |
|||
|
|||
for i, (upper, lower) in enumerate(self.staticItem["tuplets"]): |
|||
tuplet = GuiTupletNumber(upper, lower) |
|||
tuplet.setParentItem(self) |
|||
tuplet.setPos(2, (i+1)*-4-constantsAndConfigs.stafflineGap*2) |
|||
|
|||
class GuiKeySignature(GuiItem): |
|||
def __init__(self, staticItem): |
|||
super(GuiKeySignature, self).__init__(staticItem) |
|||
self.createGraphicItemsFromData() |
|||
|
|||
def createGraphicItemsFromData(self): |
|||
"""staticItems accidentalsOnLines is ordered. We receive the |
|||
correct accidental order from left to right and only need |
|||
to place the gaps""" |
|||
for i, (accidentalOnLine, pitchMod) in enumerate(self.staticItem["accidentalsOnLines"]): |
|||
glyph = GuiKeySignature.accidentals[pitchMod]() |
|||
glyph.setParentItem(self) |
|||
glyph.setPos(constantsAndConfigs.magicPixel + i*1.5*constantsAndConfigs.magicPixel, constantsAndConfigs.stafflineGap * accidentalOnLine / 2) #These accidental graphics are shifted to the left in the .svg so they can be placed just as noteheads and match automatically. So we set them +x here. |
|||
|
|||
if not self.staticItem["accidentalsOnLines"]: |
|||
#No accidentals in the keysig (C-Major, D-Dorian etc.) gets a big natural sign. |
|||
self.bigNatural = GuiKeySignature.accidentals[0]() #"big natural"... hö hö hö hö |
|||
self.bigNatural.setParentItem(self) |
|||
self.bigNatural.setPos(constantsAndConfigs.magicPixel, constantsAndConfigs.stafflineGap * -1) |
|||
|
|||
self.rootGlyph = QtWidgets.QGraphicsSimpleTextItem(constantsAndConfigs.baseNotes[self.staticItem["root"]]) |
|||
self.rootGlyph.setParentItem(self) |
|||
self.rootGlyph.setPos(constantsAndConfigs.negativeMagicPixel, 2*constantsAndConfigs.stafflineGap) |
|||
|
|||
|
|||
accidentals = { #QGraphicsItems can only be used once so we have to save a class constructor here instead of an instance. |
|||
0 : lambda: QtSvg.QGraphicsSvgItem(":svg/accidentalsNatural.svg"), #normaly we don't receive those, but it is possible to ask for explicit naturals in the backend and in lilypond. |
|||
10 : lambda: QtSvg.QGraphicsSvgItem(":svg/accidentalsSharp.svg"), |
|||
20 : lambda: QtSvg.QGraphicsSvgItem(":svg/accidentalsDoublesharp.svg"), |
|||
-10 : lambda: QtSvg.QGraphicsSvgItem(":svg/accidentalsFlat.svg"), |
|||
-20 : lambda: QtSvg.QGraphicsSvgItem(":svg/accidentalsFlatFlat.svg"), |
|||
} |
|||
|
|||
class GuiClef(GuiItem): |
|||
def __init__(self, staticItem): |
|||
super(GuiClef, self).__init__(staticItem) |
|||
self.createGraphicItemsFromData() |
|||
|
|||
def createGraphicItemsFromData(self): |
|||
self.glyph = GuiClef.clefs[self.staticItem["clef"]]() |
|||
self.glyph.setParentItem(self) |
|||
self.glyph.setPos(-5, -5 * constantsAndConfigs.stafflineGap) |
|||
|
|||
#self.text = QtWidgets.QGraphicsSimpleTextItem(self.staticItem["clef"].title()) |
|||
#self.text.setParentItem(self) |
|||
#self.text.setPos(0, -5 * constantsAndConfigs.stafflineGap) |
|||
|
|||
self.marker = GuiPositionMarker(-5) |
|||
self.marker.setParentItem(self) |
|||
self.marker.setPos(0,0) |
|||
|
|||
clefs = { #QGraphicsItems can only be used once so we have to save a class constructor here instead of an instance. |
|||
"treble" : lambda: QtSvg.QGraphicsSvgItem(":svg/clefTreble.svg"), |
|||
"treble_8" : lambda: QtSvg.QGraphicsSvgItem(":svg/clefTreble_8.svg"), |
|||
"treble^8" : lambda: QtSvg.QGraphicsSvgItem(":svg/clefTreble^8.svg"), |
|||
"bass" : lambda: QtSvg.QGraphicsSvgItem(":svg/clefBass.svg"), |
|||
"bass_8" : lambda: QtSvg.QGraphicsSvgItem(":svg/clefBass_8.svg"), |
|||
"alto" : lambda: QtSvg.QGraphicsSvgItem(":svg/clefAlto.svg"), |
|||
"midiDrum" : lambda: QtSvg.QGraphicsSvgItem(":svg/clefPercussion.svg"), |
|||
"percussion" : lambda: QtSvg.QGraphicsSvgItem(":svg/clefPercussion.svg"), |
|||
} |
|||
|
|||
class GuiTimeSignature(GuiItem): |
|||
def __init__(self, staticItem): |
|||
raise Exception("Don't use this item. It will be deleted in future versions. Use a Metrical Instruction") |
|||
super().__init__(staticItem) |
|||
self.setPos(0, constantsAndConfigs.stafflineGap) |
|||
self.createGraphicItemsFromData() |
|||
|
|||
def createGraphicItemsFromData(self): |
|||
#higher number, how many notes |
|||
for i, charInt in enumerate(str(self.staticItem["nominator"])): |
|||
n = GuiTimeSignature.digits[int(charInt)] |
|||
charInt = QtSvg.QGraphicsSvgItem() |
|||
charInt.setSharedRenderer(GuiTimeSignature.renderer) |
|||
charInt.setElementId(n) |
|||
charInt.setPos(i*2*constantsAndConfigs.magicPixel, -1*constantsAndConfigs.stafflineGap) |
|||
charInt.setParentItem(self) |
|||
|
|||
#lower number, which note type |
|||
for i, charInt in enumerate(str(self.staticItem["denominator"])): #2^n |
|||
n = GuiTimeSignature.digits[int(charInt)] |
|||
charInt = QtSvg.QGraphicsSvgItem() |
|||
charInt.setSharedRenderer(GuiTimeSignature.renderer) |
|||
charInt.setElementId(n) |
|||
charInt.setPos(i*2*constantsAndConfigs.magicPixel, constantsAndConfigs.stafflineGap) |
|||
charInt.setParentItem(self) |
|||
|
|||
renderer = QtSvg.QSvgRenderer(":svg/numbers.svg") |
|||
digits = "zero one two three four five six seven eight nine".split() #a list |
|||
|
|||
|
|||
class GuiMetricalInstruction(GuiItem): |
|||
def __init__(self, staticItem): |
|||
super().__init__(staticItem) |
|||
|
|||
if staticItem["oneMeasureInTicks"] == 0: |
|||
#Basically just a manual barline |
|||
displayString = "Metrical Off" |
|||
markerPosition = -2 |
|||
markerHeight = 4 |
|||
|
|||
else: |
|||
#Format the Tree of Instructions |
|||
realTree = eval(staticItem["treeOfInstructions"]) |
|||
if not type(realTree[0]) is tuple: #first order measure. like 3/4 or 2/4 or anything with just one stressed position |
|||
realTree = (realTree, ) #created nested iterator |
|||
r = self.treeToPrettyString(realTree) |
|||
r = r.__repr__()[1:-1].replace("'", "") |
|||
onlyTopLevelElements = not any(isinstance(el, list) for el in r) |
|||
if onlyTopLevelElements: |
|||
r = r.replace(",", "") |
|||
|
|||
displayString = "Metrical " + r |
|||
markerHeight = -7 |
|||
markerPosition = 1 |
|||
|
|||
self.text = QtWidgets.QGraphicsSimpleTextItem(displayString) |
|||
self.text.setParentItem(self) |
|||
self.text.setPos(0, -7 * constantsAndConfigs.stafflineGap) |
|||
|
|||
self.marker = GuiPositionMarker(markerHeight, markerPosition) |
|||
self.marker.setParentItem(self) |
|||
self.marker.setPos(0,0) |
|||
|
|||
def treeToPrettyString(self, tree): |
|||
"""Convert a metrical instruction into a pretty string with note symbols |
|||
For example |
|||
((53760, 53760), (53760, 53760)) |
|||
which is ((D4, D4), (D4, D4)) |
|||
|
|||
into |
|||
2𝅘𝅥 2𝅘𝅥 |
|||
|
|||
""" |
|||
result = [] |
|||
|
|||
for element in tree: |
|||
if type(element) is tuple: |
|||
l = self.treeToPrettyString(element) |
|||
l = ["{}{}".format(sum(1 for e in grouper), note) for note, grouper in groupby(l)] |
|||
if len(l) == 1: |
|||
l = l[0] #makes formatting clearerm |
|||
result.append(l) |
|||
else: |
|||
assert type(element) is int |
|||
noteString = constantsAndConfigs.realNoteDisplay[element] |
|||
result.append(noteString) |
|||
return result |
|||
|
|||
|
|||
class GuiBlockEndMarker(GuiItem): |
|||
def __init__(self, staticItem): |
|||
super(GuiBlockEndMarker, self).__init__(staticItem) |
|||
self.glyph = QtSvg.QGraphicsSvgItem(":svg/blockEnd.svg") |
|||
self.createGraphicItemsFromData() |
|||
|
|||
def createGraphicItemsFromData(self): |
|||
self.glyph.setParentItem(self) |
|||
|
|||
class GuiDynamicSignature(GuiItem): |
|||
def __init__(self, staticItem): |
|||
super(GuiDynamicSignature, self).__init__(staticItem) |
|||
self.glyph = QtWidgets.QGraphicsSimpleTextItem(staticItem["keyword"]) |
|||
self.createGraphicItemsFromData() |
|||
|
|||
def createGraphicItemsFromData(self): |
|||
self.glyph.setPos(0, 5*constantsAndConfigs.stafflineGap) |
|||
self.glyph.setParentItem(self) |
|||
|
|||
class GuiMultiMeasureRest(GuiItem): |
|||
def __init__(self, staticItem): |
|||
super(GuiMultiMeasureRest, self).__init__(staticItem) |
|||
self.createGraphicItemsFromData() |
|||
|
|||
def createGraphicItemsFromData(self): |
|||
w = self.staticItem["completeDuration"] / constantsAndConfigs.ticksToPixelRatio - constantsAndConfigs.magicPixel*2 |
|||
|
|||
if int(self.staticItem["numberOfMeasures"]) == self.staticItem["numberOfMeasures"]: #just an int? |
|||
numberOfMeasuresString = str(int(self.staticItem["numberOfMeasures"])) |
|||
else: |
|||
numberOfMeasuresString = str(self.staticItem["numberOfMeasures"]) #include the decimal dot |
|||
|
|||
rect = QtWidgets.QGraphicsRectItem(constantsAndConfigs.magicPixel,0, w, constantsAndConfigs.stafflineGap) #x,y, w, h |
|||
rect.setBrush(QtCore.Qt.black) |
|||
rect.setParentItem(self) |
|||
self.rect = rect #for stretching |
|||
|
|||
leftBlock = QtWidgets.QGraphicsRectItem(2, -1* constantsAndConfigs.stafflineGap, constantsAndConfigs.magicPixel, 3*constantsAndConfigs.stafflineGap) |
|||
leftBlock.setBrush(QtCore.Qt.black) |
|||
leftBlock.setParentItem(self) |
|||
|
|||
rightBlock = QtWidgets.QGraphicsRectItem(0, -1* constantsAndConfigs.stafflineGap, constantsAndConfigs.magicPixel, 3*constantsAndConfigs.stafflineGap) #x, y, w, h |
|||
rightBlock.setBrush(QtCore.Qt.black) |
|||
#rightBlock.setPos(w,0) |
|||
rightBlock.setPos(rect.rect().right(),0) |
|||
rightBlock.setParentItem(self) |
|||
self.rightBlock = rightBlock #for stretching |
|||
|
|||
for i, char in enumerate(numberOfMeasuresString): |
|||
if char == ".": |
|||
continue #TODO: Maybe warn about that? MMR below 1 are not valid but they are needed for editing purposes. Sometimes you need to go through such a value. |
|||
else: |
|||
n = GuiTimeSignature.digits[int(char)] |
|||
char = QtSvg.QGraphicsSvgItem() |
|||
char.setSharedRenderer(GuiTimeSignature.renderer) |
|||
char.setElementId(n) |
|||
#char.setPos(i*4*constantsAndConfigs.magicPixel + 4* constantsAndConfigs.magicPixel, -3*constantsAndConfigs.stafflineGap) |
|||
char.setPos(leftBlock.rect().width(), -3*constantsAndConfigs.stafflineGap) |
|||
char.setScale(1.5) |
|||
char.setParentItem(self) |
|||
|
|||
self.setPos(0, -3 * constantsAndConfigs.stafflineGap) |
|||
|
|||
def stretchXCoordinates(self, factor): |
|||
"""Reposition the items on the X axis. |
|||
Call goes through all parents/children, starting from ScoreView._stretchXCoordinates. |
|||
Docstring there.""" |
|||
stretchRect(self.rect, factor) |
|||
self.rightBlock.setX(self.rightBlock.pos().x() * factor) |
|||
#self.char.setX(self.char.pos().x() * factor) |
|||
|
|||
class GuiLegatoSlur(GuiItem): |
|||
def __init__(self, staticItem): |
|||
super().__init__(staticItem) |
|||
if staticItem["slur"] == "open": |
|||
self.glyph = QtWidgets.QGraphicsSimpleTextItem("(") |
|||
self.glyph.setPos(2,-5*constantsAndConfigs.stafflineGap) |
|||
else: |
|||
assert staticItem["slur"] == "close" |
|||
self.glyph = QtWidgets.QGraphicsSimpleTextItem(")") |
|||
self.glyph.setPos(-4,-5*constantsAndConfigs.stafflineGap) |
|||
|
|||
self.createGraphicItemsFromData() |
|||
|
|||
def createGraphicItemsFromData(self): |
|||
#self.glyph.setPos(0, -5*constantsAndConfigs.stafflineGap) |
|||
self.glyph.setParentItem(self) |
|||
|
|||
class GuiGenericText(GuiItem): |
|||
def __init__(self, staticItem): |
|||
super().__init__(staticItem) |
|||
self.glyph = QtWidgets.QGraphicsSimpleTextItem(staticItem["UIstring"]) |
|||
self.glyph.setPos(0, -5*constantsAndConfigs.stafflineGap) |
|||
self.glyph.setParentItem(self) |
|||
|
|||
def staticItem2Item(staticItem): |
|||
typ = staticItem["type"] |
|||
if typ is "Chord": |
|||
return GuiChord(staticItem) |
|||
elif typ is "Rest": |
|||
return GuiRest(staticItem) |
|||
elif typ is "LegatoSlur": |
|||
return GuiLegatoSlur(staticItem) |
|||
elif typ is "MultiMeasureRest": |
|||
return GuiMultiMeasureRest(staticItem) |
|||
elif typ is "DynamicSignature": |
|||
return GuiDynamicSignature(staticItem) |
|||
elif typ is "Clef": |
|||
return GuiClef(staticItem) |
|||
elif typ is "KeySignature": |
|||
return GuiKeySignature(staticItem) |
|||
#elif typ is "TimeSignature": |
|||
# return GuiTimeSignature(staticItem) |
|||
|
|||
elif typ is "MetricalInstruction": |
|||
return GuiMetricalInstruction(staticItem) |
|||
elif typ is "BlockEndMarker": |
|||
return GuiBlockEndMarker(staticItem) |
|||
elif typ in ("InstrumentChange", "ChannelChange",): |
|||
return GuiGenericText(staticItem) |
|||
else: |
|||
raise ValueError("Unknown Item Type:", staticItem) |
@ -0,0 +1,200 @@ |
|||
#! /usr/bin/env python3 |
|||
# -*- coding: utf-8 -*- |
|||
""" |
|||
Copyright 2018, Nils Hilbricht, Germany ( https://www.hilbricht.net ) |
|||
|
|||
This file is part of the Laborejo Software Suite ( https://www.laborejo.org ), |
|||
more specifically its template base application. |
|||
|
|||
The Template Base Application is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
""" |
|||
import logging; logging.info("import {}".format(__file__)) |
|||
|
|||
#Standard Library Modules |
|||
import os.path |
|||
|
|||
#Third Party Modules |
|||
from PyQt5 import QtWidgets, QtCore, QtGui |
|||
|
|||
#Template Modules |
|||
from template.qtgui.mainwindow import MainWindow as TemplateMainWindow |
|||
from template.qtgui.menu import Menu |
|||
from template.qtgui.about import About |
|||
|
|||
#Our modules |
|||
import engine.api as api |
|||
from midiinput.stepmidiinput import stepMidiInput #singleton instance |
|||
from .constantsAndConfigs import constantsAndConfigs |
|||
from .menu import MenuActionDatabase |
|||
from .scoreview import ScoreView |
|||
from .structures import GuiScore |
|||
from .trackEditor import TrackEditor |
|||
from .resources import * |
|||
|
|||
class MainWindow(TemplateMainWindow): |
|||
|
|||
def __init__(self): |
|||
"""The order of calls is very important. |
|||
The split ploint is calling the super.__init. Some functions need to be called before, |
|||
some after. |
|||
For example: |
|||
|
|||
The about dialog is created in the template main window init. So we need to set additional |
|||
help texts before that init. |
|||
""" |
|||
|
|||
#Inject more help texts in the templates About "Did You Know" field. |
|||
#About.didYouKnow is a class variable. |
|||
#Make the first three words matter! |
|||
#Do not start them all with "You can..." or "...that you can", in response to the Did you know? title. |
|||
#We use injection into the class and not a parameter because this dialog gets shown by creating an object. We can't give the parameters when this is shown via the mainWindow menu. |
|||
About.didYouKnow = [ |
|||
QtCore.QCoreApplication.translate("About", "This is an example application. Extend it to your liking. Start by editing config.py") |
|||
] + About.didYouKnow |
|||
|
|||
super().__init__() |
|||
|
|||
#New menu entries and template-menu overrides |
|||
self.menu.addMenuEntry("menuDebug", "actionRedrawAllTracks", "Redraw all Tracks") |
|||
self.menu.connectMenuEntry("actionSave", api.save) |
|||
self.menu.hideSubmenu("menuFile") |
|||
self.menu.hideSubmenu("menuGeneric") |
|||
|
|||
|
|||
api.callbacks.setCursor.append(self.updateStatusBar) #returns a dict. This get's called after loading the file so the status bar is filled on self.show |
|||
|
|||
self.initiGuiSharedDataToSave() |
|||
|
|||
#Create the Main Widgets in the Stacked Widget |
|||
self.scoreView = ScoreView(self) |
|||
self.ui.mainStackWidget.addWidget(self.scoreView) |
|||
self.ui.mainStackWidget.setCurrentIndex(self.ui.mainStackWidget.indexOf(self.scoreView)) |
|||
|
|||
self.trackEditor = QtWidgets.QScrollArea() |
|||
self.trackEditor.setWidgetResizable(True) |
|||
self.actualTrackEditor = TrackEditor(self) |
|||
self.trackEditor.setWidget(self.actualTrackEditor) |
|||
self.ui.actionData_Editor.setChecked(False) |
|||
self.ui.mainStackWidget.addWidget(self.trackEditor) |
|||
|
|||
#Bind shortcuts to actions (as init effect) |
|||
#TODO: Integrate better into template menu system. |
|||
self.menuActionDatabase = MenuActionDatabase(self) #The menu needs to be started before api.startEngine |
|||
|
|||
#Make toolbars unclosable |
|||
##self.ui.toolBar.setContextMenuPolicy(QtCore.Qt.PreventContextMenu) #only for right mouse clicks. Keyboard context menu key still works. |
|||
##self.ui.leftToolBar.setContextMenuPolicy(QtCore.Qt.PreventContextMenu) |
|||
self.setContextMenuPolicy(QtCore.Qt.NoContextMenu) #instead prevent the main window from having context menus. |
|||
|
|||
#The statusbar is used for tooltips. To make it permanent we add our own widget |
|||
self.statusLabel = QtWidgets.QLabel() |
|||
self.statusBar().insertPermanentWidget(0, self.statusLabel) |
|||
|
|||
self.scoreView.setFocus() #So the user can start typing from moment 0. |
|||
self.start() #Inherited from template main window #This shows the GUI, or not, depends on the NSM gui save setting. We need to call that after the menu, otherwise the about dialog will block and then we get new menu entries, which looks strange. |
|||
stepMidiInput.start() #imported directly. Handles everything else internally, we just need to start it after the engine somehow. Which is here. |
|||
|
|||
#Populate the left toolbar. The upper toolbar is created in menu.py |
|||
self.ui.leftToolBar.addWidget(LeftToolBarPrevailingDuration(self)) #needs stepmidiinput started |
|||
|
|||
#Now all tracks and items from a loaded backend-file are created. We can setup the initial editMode and viewPort. |
|||
self.scoreView.updateMode() #hide CCs at program start and other stuff |
|||
self.scoreView.scoreScene.grid.redrawTickGrid() #Init the grid only after everything got loaded and drawn to prevent a gap in the display. #TODO: which might be a bug. but this here works fine. |
|||
|
|||
|
|||
def initiGuiSharedDataToSave(self): |
|||
"""Called by init""" |
|||
|
|||
if not "last_export_dir" in api.session.guiSharedDataToSave: |
|||
api.session.guiSharedDataToSave["last_export_dir"] = os.path.expanduser("~") |
|||
|
|||
if "grid_opacity" in api.session.guiSharedDataToSave: |
|||
constantsAndConfigs.gridOpacity = float(api.session.guiSharedDataToSave["grid_opacity"]) |
|||
|
|||
if "grid_rhythm" in api.session.guiSharedDataToSave: |
|||
#setting this is enough. When the grid gets created it fetches the constantsAndConfigs value. |
|||
#Set only in submenus.GridRhytmEdit |
|||
constantsAndConfigs.gridRhythm = int(api.session.guiSharedDataToSave["grid_rhythm"]) |
|||
|
|||
#Stretch |
|||
if "ticks_to_pixel_ratio" in api.session.guiSharedDataToSave: |
|||
#setting this is enough. Drawing on startup uses the constantsAndConfigs value. |
|||
#Set only in ScoreView._stretchXCoordinates |
|||
constantsAndConfigs.ticksToPixelRatio = float(api.session.guiSharedDataToSave["ticks_to_pixel_ratio"]) |
|||
|
|||
if "zoom_factor" in api.session.guiSharedDataToSave: |
|||
#setting this is enough. Drawing on startup uses the constantsAndConfigs value. |
|||
#Set only in ScoreView._zoom |
|||
constantsAndConfigs.zoomFactor = float(api.session.guiSharedDataToSave["zoom_factor"]) |
|||
|
|||
def updateStatusBar(self, exportCursorDict): |
|||
"""Every cursor movement updates the statusBar message""" |
|||
c = exportCursorDict |
|||
i = c["item"] |
|||
if i: |
|||
ly = i.lilypond() |
|||
if (not ly) or len(ly) > 10: |
|||
ly = "" |
|||
else: |
|||
ly = "Lilypond: <b>{}</b>".format(ly.replace("<", "<").replace(">", ">")) |
|||
itemMessage = "Item: <b>{}</b> {}".format(i.__class__.__name__, ly) |
|||
|
|||
else: |
|||
itemMessage = "" #Appending |
|||
|
|||
positionMessage = "Pos: <b>{}</b> Ticks: <b>{}</b> Pitch: <b>{}</b>".format(c["position"], c["tickindex"], c["lilypondPitch"]) |
|||
|
|||
message = "{} | {}".format(itemMessage, positionMessage) |
|||
#self.statusBar().showMessage(message) #overriden by tool tips, even empty ones |
|||
self.statusLabel.setText(message) |
|||
|
|||
|
|||
def toggleMainView(self): |
|||
if self.ui.actionData_Editor.isChecked(): |
|||
self.ui.mainStackWidget.setCurrentIndex(self.ui.mainStackWidget.indexOf(self.trackEditor)) |
|||
self.scoreView.setEnabled(False) #disables shortcut like cursor movement, but not all of them. |
|||
self.menuActionDatabase.writeProtection(True) |
|||
self.trackEditor.setEnabled(True) |
|||
else: |
|||
self.ui.mainStackWidget.setCurrentIndex(self.ui.mainStackWidget.indexOf(self.scoreView)) |
|||
self.scoreView.setEnabled(True) |
|||
self.menuActionDatabase.writeProtection(False) |
|||
self.scoreView.updateMode() |
|||
self.trackEditor.setEnabled(False) |
|||
|
|||
class LeftToolBarPrevailingDuration(QtWidgets.QLabel): |
|||
def __init__(self, mainWindow): |
|||
super().__init__(self.makeText(api.D4)) |
|||
self.mainWindow = mainWindow |
|||
|
|||
#self.setFont(constantsAndConfigs.musicFont) #TODO replace with svg |
|||
api.callbacks.prevailingBaseDurationChanged.append(self.changed) |
|||
|
|||
def makeText(self, baseDuration): |
|||
if not stepMidiInput.midiInIsActive: |
|||
return "" |
|||
|
|||
labelText = "<font size=6>" |
|||
for i in (api.D1, api.D2, api.D4, api.D8, api.D16): #,api.DB, api.DL): |
|||
if i == baseDuration: |
|||
labelText += "<font color='cyan'>" |
|||
labelText += constantsAndConfigs.realNoteDisplay[i] |
|||
if i == baseDuration: |
|||
labelText += "</font>" |
|||
labelText += "<br>" |
|||
labelText += "</font>" |
|||
return labelText |
|||
|
|||
def changed(self, newBaseDuration): |
|||
self.setText(self.makeText(newBaseDuration)) |
@ -0,0 +1,570 @@ |
|||
#! /usr/bin/env python3 |
|||
# -*- coding: utf-8 -*- |
|||
""" |
|||
Copyright 2017, Nils Hilbricht, Germany ( https://www.hilbricht.net ) |
|||
|
|||
This file is part of Laborejo ( https://www.laborejo.org ) |
|||
|
|||
Laborejo is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
""" |
|||
|
|||
from PyQt5 import QtCore, QtGui, QtWidgets |
|||
import os.path |
|||
import engine.api as api |
|||
from midiinput.stepmidiinput import stepMidiInput #singleton instance |
|||
from .constantsAndConfigs import constantsAndConfigs |
|||
from .submenus import SecondaryClefMenu, SecondaryKeySignatureMenu, SecondaryDynamicsMenu, SecondaryMetricalInstructionMenu, SecondaryTempoChangeMenu, SecondaryTemporaryTempoChangeMenu, SecondarySplitMenu, TransposeMenu, pedalNoteChooser, SecondaryProperties, SecondaryProgramChangeMenu, SecondaryChannelChangeMenu |
|||
|
|||
class ModalKeys(object): |
|||
def __init__(self): |
|||
pass |
|||
|
|||
def keyRest(self): |
|||
if stepMidiInput.midiInIsActive: |
|||
baseDuration = api.session.data.cursor.prevailingBaseDuration |
|||
api.insertRest(baseDuration) |
|||
#else: deactivated. |
|||
|
|||
def key1(self): |
|||
if stepMidiInput.midiInIsActive: api.prevailing1() |
|||
else: api.insert1() |
|||
|
|||
def key2(self): |
|||
if stepMidiInput.midiInIsActive: api.prevailing2() |
|||
else: api.insert2() |
|||
|
|||
def key3(self): |
|||
if stepMidiInput.midiInIsActive: api.prevailing4() |
|||
else: api.insert4() |
|||
|
|||
def key4(self): |
|||
if stepMidiInput.midiInIsActive: api.prevailing8() |
|||
else: api.insert8() |
|||
|
|||
def key5(self): |
|||
if stepMidiInput.midiInIsActive: api.prevailing16() |
|||
else: api.insert16() |
|||
|
|||
def key6(self): |
|||
if stepMidiInput.midiInIsActive: api.prevailing32() |
|||
else: api.insert32() |
|||
|
|||
def key7(self): |
|||
if stepMidiInput.midiInIsActive: api.prevailingBrevis() |
|||
else: api.insertBrevis() |
|||
|
|||
def key8(self): |
|||
if stepMidiInput.midiInIsActive: api.prevailingLonga() |
|||
else: api.insertLonga() |
|||
|
|||
def key9(self): |
|||
pass |
|||
|
|||
def key0(self): |
|||
pass |
|||
|
|||
class MenuActionDatabase(object): |
|||
"""There are different edit and view modes in Laborejo-Qt: |
|||
|
|||
Main Program |
|||
ScoreView |
|||
NoteEdit |
|||
Noteheads |
|||
Rectangles |
|||
BlockEdit |
|||
Transparent Block Handles |
|||
CCEdit |
|||
CC 0 |
|||
CC 1 |
|||
... |
|||
CC 127 |
|||
DataEditor |
|||
|
|||
|
|||
Each level has shorcuts associated with them which depend on the context. |
|||
To implement those we use the qt shortcut contexts. See QAction docs. |
|||
|
|||
Additionaly there is midi-in and the modal number keys, which are |
|||
connected to each other. Without midi in the modal keys insert notes, |
|||
in midi mode they choose the duration for the next midi key. |
|||
""" |
|||
|
|||
def __init__(self, mainWindow): |
|||
self.mainWindow = mainWindow |
|||
self.modalKeys = ModalKeys() |
|||
|
|||
modes = QtWidgets.QActionGroup(self.mainWindow) |
|||
modes.addAction(self.mainWindow.ui.actionNotation_Mode) |
|||
modes.addAction(self.mainWindow.ui.actionBlock_Mode) |
|||
modes.addAction(self.mainWindow.ui.actionCC_Mode) |
|||
self.mainWindow.ui.actionNotation_Mode.setChecked(True) |
|||
|
|||
self.rememberMidi = stepMidiInput.midiInIsActive |
|||
|
|||
api.callbacks.historyChanged.append(self.renameUndoRedoByHistory) |
|||
api.callbacks.metronomeChanged.append(lambda v: self.mainWindow.ui.actionMetronome_Enabled.setChecked(v["enabled"])) #accompanied by a normal menu action down below |
|||
|
|||
self.actionsWithoutMenu = { #these are only available in Note Edit Mode, not in CC Edit Mode etc. but have no menu entry. |
|||
#Of course these actions have shortcuts, they are in a menu, visible and editable in qt-Designer, which gets hidden on program start. |
|||
self.mainWindow.ui.actionLeft : api.left, |
|||
self.mainWindow.ui.actionRight : api.right, |
|||
self.mainWindow.ui.actionSelectLeft : api.selectLeft, |
|||
self.mainWindow.ui.actionSelectRight : api.selectRight, |
|||
|
|||
self.mainWindow.ui.actionMeasureLeft : api.measureLeft, |
|||
self.mainWindow.ui.actionMeasureRight : api.measureRight, |
|||
self.mainWindow.ui.actionSelectMeasureLeft : api.selectMeasureLeft, |
|||
self.mainWindow.ui.actionSelectMeasureRight : api.selectMeasureRight, |
|||
|
|||
|
|||
self.mainWindow.ui.actionBlockLeft : api.blockLeft, |
|||
self.mainWindow.ui.actionBlockRight : api.blockRight, |
|||
self.mainWindow.ui.actionSelectBlockLeft : api.selectBlockLeft, |
|||
self.mainWindow.ui.actionSelectBlockRight : api.selectBlockRight, |
|||
|
|||
self.mainWindow.ui.actionHead : api.head, |
|||
self.mainWindow.ui.actionTail : api.tail, |
|||
self.mainWindow.ui.actionSelectHead : api.selectHead, |
|||
self.mainWindow.ui.actionSelectTail : api.selectTail, |
|||
|
|||
self.mainWindow.ui.actionTrackUp : api.trackUp, |
|||
self.mainWindow.ui.actionTrackDown : api.trackDown, |
|||
self.mainWindow.ui.actionTrackFirst : api.trackFirst, |
|||
self.mainWindow.ui.actionTrackLast : api.trackLast, |
|||
self.mainWindow.ui.actionSelectTrackUp : api.selectTrackUp, |
|||
self.mainWindow.ui.actionSelectTrackDown : api.selectTrackDown, |
|||
self.mainWindow.ui.actionSelectTrackFirst : api.selectTrackFirst, |
|||
self.mainWindow.ui.actionSelectTrackLast : api.selectTrackLast, |
|||
|
|||
self.mainWindow.ui.actionUp : api.up, |
|||
self.mainWindow.ui.actionDown : api.down, |
|||
self.mainWindow.ui.actionUpOctave : api.upOctave, |
|||
self.mainWindow.ui.actionDownOctave : api.downOctave, |
|||
self.mainWindow.ui.actionSelectUp : api.up, #these are the same as up and down. But for a good user experience they should work if shift is pressed down as well. |
|||
self.mainWindow.ui.actionSelectDown : api.down, |
|||
self.mainWindow.ui.actionSelectUpOctave : api.upOctave, |
|||
self.mainWindow.ui.actionSelectDownOctave : api.downOctave, |
|||
|
|||
|
|||
self.mainWindow.ui.actionSelectAllTracks : api.selectAllTracks, |
|||
self.mainWindow.ui.actionSelectTrack : api.selectTrack, |
|||
self.mainWindow.ui.actionSelectMeasureColumn : api.selectMeasureColumn, |
|||
|
|||
#CC Edit Actions. Share the same shortcuts as some of the actions above. But only one of these is enabled at any time. |
|||
#Don't forget to add them to the list below: self.ccEditActions |
|||
#... |
|||
} |
|||
|
|||
self.actions = { #always available in the main window. It doesn't matter if you change to CC mode or Note Edit etc. |
|||
self.mainWindow.ui.actionToggle_Notehead_Rectangles : self.mainWindow.scoreView.toggleNoteheadsRectangles, |
|||
self.mainWindow.ui.actionData_Editor : self.mainWindow.toggleMainView, |
|||
self.mainWindow.ui.actionProperties : lambda: SecondaryProperties(self.mainWindow), |
|||
self.mainWindow.ui.actionFollow_Playhead : self.mainWindow.scoreView.toggleFollowPlayhead, |
|||
#Modes Submenu |
|||
self.mainWindow.ui.actionNotation_Mode : self.mainWindow.scoreView.updateMode, |
|||
self.mainWindow.ui.actionBlock_Mode : self.mainWindow.scoreView.updateMode, |
|||
self.mainWindow.ui.actionCC_Mode : self.mainWindow.scoreView.updateMode, |
|||
|
|||
self.mainWindow.ui.actionChange_Grid_Rhythm : self.mainWindow.scoreView.changeGridRhythm, |
|||
self.mainWindow.ui.actionAdd_Track : api.newEmptyTrack, |
|||
self.mainWindow.ui.actionDelete_Current_Track : api.deleteCurrentTrack, |
|||
self.mainWindow.ui.actionUse_Current_Track_as_Metronome : api.useCurrentTrackAsMetronome, |
|||
self.mainWindow.ui.actionMidi_In_is_Active : self.toggleMidiInIsActive, |
|||
#self.mainWindow.ui.actionZoom_In_Score : self.mainWindow.scoreView.zoomIn, |
|||
#self.mainWindow.ui.actionZoom_Out_Score : self.mainWindow.scoreView.zoomOut, |
|||
self.mainWindow.ui.actionWiden_Score_View : self.mainWindow.scoreView.widen, |
|||
self.mainWindow.ui.actionShrink_Score_View : self.mainWindow.scoreView.shrinken, |
|||
self.mainWindow.ui.actionSave : api.save, |
|||
|
|||
self.mainWindow.ui.actionShow_PDF : api.showPDF, |
|||
self.mainWindow.ui.actionExport_to_Ly : self.exportLy, |
|||
|
|||
self.mainWindow.ui.actionPlayFromBeginning : api.playFromStart, |
|||
self.mainWindow.ui.actionPlayPause : api.playPause, |
|||
self.mainWindow.ui.actionPlayFromEditCursor : api.playFromCursor, |
|||
self.mainWindow.ui.actionMetronome_Enabled : api.toggleMetronome, #toggle is enough. The callback makes sure all the checkboxes have the correct value. |
|||
} |
|||
|
|||
self.noteEditActions = { #these are only available in Note Edit Mode, not in CC Edit Mode etc. #all xxxEditActions are mutually exclusive. |
|||
self.mainWindow.ui.actionBeam : api.toggleBeam, |
|||
self.mainWindow.ui.actionRemoveBeams : api.removeBeam, |
|||
|
|||
self.mainWindow.ui.actionLegatoSlur : api.insertLegatoSlur, |
|||
self.mainWindow.ui.actionClef : SecondaryClefMenu(self.mainWindow), #no lambda for submenus. They get created here once and have a __call__ option that executes them. There is no internal state in these menus. |
|||
self.mainWindow.ui.actionKey_Signature : SecondaryKeySignatureMenu(self.mainWindow), |
|||
self.mainWindow.ui.actionDynamics : SecondaryDynamicsMenu(self.mainWindow), |
|||
self.mainWindow.ui.actionMulti_Measure_Rest : lambda: api.insertMultiMeasureRest(1), |
|||
self.mainWindow.ui.actionMetrical_Instruction : SecondaryMetricalInstructionMenu(self.mainWindow), |
|||
|
|||
|
|||
self.mainWindow.ui.actionTempo_Change : lambda: SecondaryTempoChangeMenu(self.mainWindow), |
|||
self.mainWindow.ui.actionDelete_Tempo_Change : api.removeCurrentTempoItem, |
|||
self.mainWindow.ui.actionTemporary_Tempo_Change : SecondaryTemporaryTempoChangeMenu(self.mainWindow), |
|||
|
|||
self.mainWindow.ui.actionSharpen_Note : api.sharpenNote, |
|||
self.mainWindow.ui.actionFlatten_Note : api.flattenNote, |
|||
self.mainWindow.ui.actionStep_Up : api.stepUp, |
|||
self.mainWindow.ui.actionStep_Down : api.stepDown, |
|||
self.mainWindow.ui.actionOctave_Up : api.stepUpOctave, |
|||
self.mainWindow.ui.actionOctave_Down : api.stepDownOctave, |
|||
self.mainWindow.ui.actionTransposeChord : lambda: TransposeMenu(self.mainWindow, "item"), |
|||
|
|||
|
|||
self.mainWindow.ui.actionVelocityMore : lambda: self.mainWindow.scoreView._switchToRectanglesForFunction(api.moreVelocity), |
|||
self.mainWindow.ui.actionVelocityLess : lambda: self.mainWindow.scoreView._switchToRectanglesForFunction(api.lessVelocity), |
|||
self.mainWindow.ui.actionDurationModMore : lambda: self.mainWindow.scoreView._switchToRectanglesForFunction(api.moreDuration), |
|||
self.mainWindow.ui.actionDurationModLess : lambda: self.mainWindow.scoreView._switchToRectanglesForFunction(api.lessDuration), |
|||
self.mainWindow.ui.actionReset_Velocity_Duration_Mod : lambda: self.mainWindow.scoreView._switchToRectanglesForFunction(api.resetDurationVelocity), |
|||
|
|||
self.mainWindow.ui.actionAugment : api.augment, |
|||
self.mainWindow.ui.actionDiminish : api.diminish, |
|||
self.mainWindow.ui.actionDots : api.dot, |
|||
self.mainWindow.ui.actionTriplet : api.triplet, |
|||
#self.mainWindow.ui.actionCustomTuplet : something with a GUI that allows any type and number of nested tuplets, #TODO |
|||
self.mainWindow.ui.actionStaccato : api.staccato, |
|||
self.mainWindow.ui.actionTenuto : api.tenuto, |
|||
self.mainWindow.ui.actionTie : api.tie, |
|||
self.mainWindow.ui.actionSplit_in_2 : lambda: api.split(2), |
|||
self.mainWindow.ui.actionSplit_in_3 : lambda: api.split(3), |
|||
self.mainWindow.ui.actionCustom_Split : SecondarySplitMenu(self.mainWindow), #no lambda for submenus. They get created here once and have a __call__ option that executes them. There is no internal state in these menus. |
|||
|
|||
self.mainWindow.ui.actionMIDI_Channel_Plus : api.midiRelativeChannelPlus, |
|||
self.mainWindow.ui.actionMIDI_Channel_Minus : api.midiRelativeChannelMinus, |
|||
self.mainWindow.ui.actionMIDI_Channel_Reset : api.midiRelativeChannelReset, |
|||
|
|||
self.mainWindow.ui.actionAppend_Block : api.appendBlock, |
|||
self.mainWindow.ui.actionSplit_Current_Block : api.splitBlock, |
|||
self.mainWindow.ui.actionJoin_with_next_Block : api.joinBlock, |
|||
self.mainWindow.ui.actionDuplicate : api.duplicateCurrentBlock, |
|||
self.mainWindow.ui.actionCreate_Linked_Copy : api.duplicateContentLinkCurrentBlock, |
|||
self.mainWindow.ui.actionUnlink_Current_Block : api.unlinkCurrentBlock, |
|||
self.mainWindow.ui.actionDelete_Current_Block : api.deleteCurrentBlock, |
|||
|
|||
self.mainWindow.ui.actionTranspose_Score : lambda: TransposeMenu(self.mainWindow, "score"), |
|||
self.mainWindow.ui.actionDelete_All_Empty_Blocks : api.deleteEmptyBlocks, |
|||
|
|||
self.mainWindow.ui.actionDelete : api.delete, |
|||
self.mainWindow.ui.actionBackspace : api.backspace, |
|||
self.mainWindow.ui.actionAddCursorNoteToChord : api.addCursorNoteToChord, |
|||
self.mainWindow.ui.actionDeleteCursorNoteFromChord : api.deleteCursorNoteFromChord, |
|||
self.mainWindow.ui.actionCut : api.cutObjects, |
|||
self.mainWindow.ui.actionCopy : api.copyObjects, |
|||
self.mainWindow.ui.actionPaste : api.pasteObjects, |
|||
self.mainWindow.ui.actionDuplicateItem : api.duplicate, |
|||
self.mainWindow.ui.actionUndo : api.undo, |
|||
self.mainWindow.ui.actionRedo : api.redo, |
|||
|
|||
self.mainWindow.ui.actionRedrawAllTracks : api.updateCallbackAllTracks, |
|||
|
|||
#Toolbox |
|||
#Note Generation |
|||
self.mainWindow.ui.actionPedalNotes : lambda: pedalNoteChooser(self.mainWindow), |
|||
self.mainWindow.ui.actionRandom_chromatic_in_clef_range: api.insertRandomChromaticInClefRange, |
|||
self.mainWindow.ui.actionRandom_in_scale_in_clef_range: api.insertRandomFromScaleInClefRange, |
|||
self.mainWindow.ui.actionRandom_pitch_from_clipboard: api.insertRandomFromClipboard, |
|||
self.mainWindow.ui.actionRandom_in_scale_in_octave_around_cursor: api.insertRandomFromScaleAuthenticModeCursor, |
|||
self.mainWindow.ui.actionRandom_in_scale_in_cursor_plus_octave: api.insertRandomFromScaleHypoModeCursor, |
|||
self.mainWindow.ui.actionMirror_around_Cursor: api.mirrorAroundCursor, |
|||
|
|||
#Note Sorting |
|||
self.mainWindow.ui.actionRandom: api.nothing, |
|||
self.mainWindow.ui.actionReverse: api.nothing, |
|||
self.mainWindow.ui.actionAscending: api.nothing, |
|||
self.mainWindow.ui.actionDescending: api.nothing, |
|||
#Midi |
|||
self.mainWindow.ui.actionInstrument_Change: SecondaryProgramChangeMenu(self.mainWindow), #no lambda for submenus. They get created here once and have a __call__ option that executes them. There is no internal state in these menus. |
|||
self.mainWindow.ui.actionChannel_Change: SecondaryChannelChangeMenu(self.mainWindow), #no lambda for submenus. They get created here once and have a __call__ option that executes them. There is no internal state in these menus. |
|||
} |
|||
|
|||
self.modalActions = { #these are only available in Note Edit Mode, not in CC Edit Mode etc. |
|||
self.mainWindow.ui.actionModal1 : self.modalKeys.key1, |
|||
self.mainWindow.ui.actionModal2 : self.modalKeys.key2, |
|||
self.mainWindow.ui.actionModal3 : self.modalKeys.key3, |
|||
self.mainWindow.ui.actionModal4 : self.modalKeys.key4, |
|||
self.mainWindow.ui.actionModal5 : self.modalKeys.key5, |
|||
self.mainWindow.ui.actionModal6 : self.modalKeys.key6, |
|||
self.mainWindow.ui.actionModal7 : self.modalKeys.key7, #brevis |
|||
self.mainWindow.ui.actionModal8 : self.modalKeys.key8, #longa |
|||
|
|||
self.mainWindow.ui.actionPrevailingRest : self.modalKeys.keyRest, |
|||
|
|||
self.mainWindow.ui.actionShift_modal1 : api.insertRest1, |
|||
self.mainWindow.ui.actionShift_modal2 : api.insertRest2, |
|||
self.mainWindow.ui.actionShift_modal3 : api.insertRest4, |
|||
self.mainWindow.ui.actionShift_modal4 : api.insertRest8, |
|||
self.mainWindow.ui.actionShift_modal5 : api.insertRest16, |
|||
self.mainWindow.ui.actionShift_modal6 : api.insertRest32, |
|||
#self.mainWindow.ui.actionShift_modal7 : api.insertRestBrevis, |
|||
#self.mainWindow.ui.actionShift_modal8 : api.insertRestLonga, |
|||
#self.mainWindow.ui.actionShift_modal7 : api.insertRest64, |
|||
#self.mainWindow.ui.actionShift_modal8 : api.insertRest128, |
|||
} |
|||
|
|||
self.nonEditingMusicActions = [ #These are available in CC Edit Mode, Note Edit mode etc. |
|||
self.mainWindow.ui.actionPlayFromBeginning, |
|||
self.mainWindow.ui.actionPlayPause, |
|||
self.mainWindow.ui.actionPlayFromEditCursor, |
|||
self.mainWindow.ui.actionToggle_Notehead_Rectangles, |
|||
self.mainWindow.ui.actionData_Editor, |
|||
self.mainWindow.ui.actionFollow_Playhead, |
|||
self.mainWindow.ui.actionNotation_Mode, |
|||
self.mainWindow.ui.actionBlock_Mode, |
|||
self.mainWindow.ui.actionCC_Mode, |
|||
self.mainWindow.ui.actionChange_Grid_Rhythm, |
|||
self.mainWindow.ui.actionAdd_Track, |
|||
self.mainWindow.ui.actionUndo, |
|||
self.mainWindow.ui.actionRedo, |
|||
#self.mainWindow.ui.actionZoom_In_Score, |
|||
#self.mainWindow.ui.actionZoom_Out_Score, |
|||
self.mainWindow.ui.actionWiden_Score_View, |
|||
self.mainWindow.ui.actionShrink_Score_View, |
|||
self.mainWindow.ui.actionSave, |
|||
|
|||
] |
|||
|
|||
self.ccEditActions = [ #only in CC edit, when editing user points. #all xxxEditActions are mutually exclusive. These are defined in non-menu actions above. |
|||
# self.mainWindow.ui.CCactionDelete, |
|||
] |
|||
|
|||
#Prepare non-designer widgets. Designer can't put normal widgets in a toolbar, but Qt can. |
|||
#Eventhough this is a dict, which has no order, the CREATION has an order. So the first item in the dict will be the first item in the toolBar |
|||
self.extraToolBarWidgets = { |
|||
"snapToGrid" : self.mainWindow.ui.toolBar.addWidget(ToolBarSnapToGrid(mainWindow=self.mainWindow)), |
|||
"metronome" : self.mainWindow.ui.toolBar.addWidget(ToolBarMetronome(mainWindow=self.mainWindow)), |
|||
"playbackSpeed" : self.mainWindow.ui.toolBar.addWidget(ToolBarPlaybackSpeed(mainWindow=self.mainWindow)), |
|||
"ccType" : self.mainWindow.ui.toolBar.addWidget(ToolBarCCType(mainWindow=self.mainWindow)), #keep this at the end of the toolbar because it toggles visibility |
|||
} |
|||
|
|||
self.toolbarContexts = { |
|||
"notation" : [self.extraToolBarWidgets["snapToGrid"], self.extraToolBarWidgets["metronome"], self.extraToolBarWidgets["playbackSpeed"], ], |
|||
"cc" : [self.extraToolBarWidgets["snapToGrid"], self.extraToolBarWidgets["metronome"], self.extraToolBarWidgets["ccType"], self.extraToolBarWidgets["playbackSpeed"]], |
|||
"block": [self.extraToolBarWidgets["snapToGrid"], self.extraToolBarWidgets["metronome"], self.extraToolBarWidgets["playbackSpeed"], ], |
|||
} |
|||
|
|||
#Now connect all actions to functions |
|||
|
|||
for action, function in self.actions.items(): |
|||
action.triggered.connect(function) |
|||
|
|||
#all xxxEditActions are mutually exclusive. |
|||
for action, function in self.noteEditActions.items(): |
|||
action.triggered.connect(function) |
|||
|
|||
for action in self.ccEditActions: # a list. |
|||
action.setEnabled(False) #These are the same keys as noteEditActions, so we need to disable them for now. |
|||
|
|||
for action, function in self.modalActions.items(): |
|||
self.mainWindow.ui.centralwidget.addAction(action) #no actions without a widget |
|||
action.triggered.connect(function) |
|||
|
|||
#Do not connect modalActionsPrevailing yet |
|||
|
|||
for action, function in self.actionsWithoutMenu.items(): |
|||
self.mainWindow.ui.centralwidget.addAction(action) #no actions without a widget |
|||
action.triggered.connect(function) |
|||
|
|||
self.mainWindow.ui.menubar.removeAction(self.mainWindow.ui.menuGeneric.menuAction()) #thats why we added the actions to the centralwidget above. |
|||
self.loadToolbarContext("notation") |
|||
|
|||
def toggleMidiInIsActive(self): |
|||
"""That is: "toggle midiInIsActive", not "toggleMidiIn is active?" """ |
|||
stepMidiInput.toggleMidiIn() |
|||
self.mainWindow.ui.actionPrevailingRest.setEnabled(stepMidiInput.midiInIsActive) |
|||
self.mainWindow.ui.actionMidi_In_is_Active.setChecked(stepMidiInput.midiInIsActive) |
|||
|
|||
def setMusicEditingEnabled(self, boolean): |
|||
"""This is the "reset to standard" functions for shortcuts. |
|||
If you want to enable other keys, such as CC editing keys, |
|||
you have to call another function after this one""" |
|||
allActions = list(self.actions.keys()) + list(self.modalActions.keys()) + list(self.actionsWithoutMenu.keys()) + list(self.noteEditActions.keys()) |
|||
|
|||
for x in self.nonEditingMusicActions: |
|||
allActions.remove(x) |
|||
|
|||
for editAction in allActions: |
|||
editAction.setEnabled(boolean) |
|||
|
|||
for action in self.ccEditActions: # Call this after enabling stuff in the for-loop above. |
|||
action.setEnabled(False) #These are the same keys as noteEditActions, so we need to disable them for now. Maybe they are enabled later again, after this function. |
|||
|
|||
self.mainWindow.ui.actionPrevailingRest.setEnabled(boolean and stepMidiInput.midiInIsActive) |
|||
|
|||
def noteEditMode(self): |
|||
self.setMusicEditingEnabled(True) |
|||
|
|||
def ccEditMode(self): |
|||
self.setMusicEditingEnabled(False) |
|||
for action in self.ccEditActions: # a list. |
|||
action.setEnabled(True) #These are the same keys as noteEditActions, which are disabled above. |
|||
|
|||
def writeProtection(self, boolean): |
|||
"""Artificial read only mode that was designed for the mouse-only track editor etc. |
|||
When zoomed out the user should be able to use the mouse but the cursor is hidden and |
|||
""" |
|||
|
|||
if boolean: |
|||
#Switch off midi input |
|||
if stepMidiInput.midiInIsActive: |
|||
self.rememberMidi = stepMidiInput.midiInIsActive |
|||
stepMidiInput.toggleMidiIn() |
|||
assert not stepMidiInput.midiInIsActive |
|||
self.mainWindow.ui.actionPrevailingRest.setEnabled(stepMidiInput.midiInIsActive) |
|||
self.mainWindow.ui.actionMidi_In_is_Active.setChecked(stepMidiInput.midiInIsActive) |
|||
#Deactivate Note Editing Actions |
|||
self.setMusicEditingEnabled(False) |
|||
self.mainWindow.scoreView.scoreScene.cursor.hide() #the cursor which can be used to edit notes. Since the cursor shortcuts get disabled it cannot be changed invisbly. |
|||
else: |
|||
self.setMusicEditingEnabled(True) |
|||
self.mainWindow.scoreView.scoreScene.cursor.show() |
|||
if self.rememberMidi and not stepMidiInput.midiInIsActive: |
|||
self.toggleMidiInIsActive() |
|||
self.rememberMidi = None |
|||
|
|||
def loadToolbarContext(self, nameAsString): |
|||
"""Hide all toolbar actions and only load those |
|||
which are in the current context, defined as list""" |
|||
|
|||
assert nameAsString in constantsAndConfigs.availableEditModes |
|||
|
|||
for action in self.mainWindow.ui.toolBar.actions(): |
|||
action.setVisible(False) |
|||
|
|||
for action in self.toolbarContexts[nameAsString]: |
|||
action.setVisible(True) |
|||
|
|||
def renameUndoRedoByHistory(self, undoList, redoList): |
|||
if undoList: |
|||
self.mainWindow.ui.actionUndo.setText("Undo: {}".format(undoList[-1])) |
|||
self.mainWindow.ui.actionUndo.setEnabled(True) |
|||
else: |
|||
self.mainWindow.ui.actionUndo.setText("Undo") |
|||
self.mainWindow.ui.actionUndo.setEnabled(False) |
|||
|
|||
|
|||
if redoList: |
|||
self.mainWindow.ui.actionRedo.setText("Redo: {}".format(redoList[-1])) |
|||
self.mainWindow.ui.actionRedo.setEnabled(True) |
|||
else: |
|||
self.mainWindow.ui.actionRedo.setText("Redo") |
|||
self.mainWindow.ui.actionRedo.setEnabled(False) |
|||
|
|||
|
|||
def exportLy(self): |
|||
filename = QtWidgets.QFileDialog.getSaveFileName(self.mainWindow, "Export Lilypond Source File", self.mainWindow.settings.value("last_export_dir"), "Lilypond Source (*.ly)") |
|||
filename = filename[0] #(path, filter) |
|||
if filename: |
|||
if not os.path.splitext(filename)[1]: #no file extension given? |
|||
filename = filename + ".ly" |
|||
self.mainWindow.settings.setValue("last_export_dir", os.path.dirname(filename)) |
|||
api.exportLilypond(filename) |
|||
|
|||
|
|||
class ToolBarCCType(QtWidgets.QSpinBox): |
|||
def __init__(self, mainWindow): |
|||
super().__init__() |
|||
self.mainWindow = mainWindow |
|||
self.setRange(0,127) |
|||
self.setPrefix("CC ") |
|||
|
|||
self.valueChanged.connect(self.changed) |
|||
|
|||
#self.addItems([str(i).zfill(3) for i in range(128) ]) |
|||
|
|||
def changed(self): |
|||
constantsAndConfigs.ccViewValue = self.value() |
|||
self.mainWindow.scoreView.updateMode() #gets the new value itself from constantsAndConfigs |
|||
|
|||
class ToolBarSnapToGrid(QtWidgets.QCheckBox): |
|||
def __init__(self, mainWindow): |
|||
super().__init__("Snap to Grid") |
|||
self.setStatus() |
|||
self.stateChanged.connect(self.changed) |
|||
|
|||
constantsAndConfigs.snapToGridCallbacks.append(self.updateFromCallback) |
|||
|
|||
def setStatus(self): |
|||
if constantsAndConfigs.snapToGrid: |
|||
self.setCheckState(QtCore.Qt.Checked) |
|||
else: |
|||
self.setCheckState(QtCore.Qt.Unchecked) |
|||
|
|||
def isChecked(self): |
|||
if self.checkState() == QtCore.Qt.Checked: |
|||
return True |
|||
else: |
|||
return False |
|||
|
|||
def updateFromCallback(self): |
|||
self.blockSignals(True) |
|||
self.setStatus() |
|||
self.blockSignals(False) |
|||
|
|||
def changed(self): |
|||
constantsAndConfigs.snapToGrid = self.isChecked() #this sends a call back to all Snap to Grid in the whole program |
|||
|
|||
|
|||
class ToolBarMetronome(QtWidgets.QCheckBox): |
|||
"""Contrary to SnapToGrid this reflects a backend state, not a pure GUI one. |
|||
There is also the menu actionMetronome_Enabled , itself a normal checkbox. It's callback and |
|||
state are handled in the menuDatabase classes init.""" |
|||
def __init__(self, mainWindow): |
|||
super().__init__("Metronome") |
|||
self.setStatus() |
|||
self.stateChanged.connect(self.changed) |
|||
api.callbacks.metronomeChanged.append(self.updateFromCallback) |
|||
|
|||
def setStatus(self): |
|||
if api.isMetronomeEnabled(): |
|||
self.setCheckState(QtCore.Qt.Checked) |
|||
else: |
|||
self.setCheckState(QtCore.Qt.Unchecked) |
|||
|
|||
def isChecked(self): |
|||
if self.checkState() == QtCore.Qt.Checked: |
|||
assert api.isMetronomeEnabled(), api.isMetronomeEnabled() |
|||
return True |
|||
else: |
|||
assert not api.isMetronomeEnabled(), api.isMetronomeEnabled() |
|||
return False |
|||
|
|||
def updateFromCallback(self, exportDict): |
|||
self.blockSignals(True) |
|||
self.setStatus() |
|||
assert self.isChecked() == exportDict["enabled"], (self.isChecked(), exportDict["enabled"]) |
|||
self.setText("Metronome: " + exportDict["trackName"]) |
|||
self.blockSignals(False) |
|||
|
|||
def changed(self, value): |
|||
api.enableMetronome(value) #triggers callback |
|||
|
|||
|
|||
class ToolBarPlaybackSpeed(QtWidgets.QDoubleSpinBox): |
|||
def __init__(self, mainWindow): |
|||
super().__init__() |
|||
self.setRange(0.1,3.0) |
|||
self.setDecimals(1) |
|||
self.setValue(1.0) |
|||
self.setSingleStep(0.1) |
|||
self.setPrefix("Tempo ×") #yes, this is the unicode multiplication sign × and not an x |
|||
api.callbacks.playbackSpeedChanged.append(self.updateFromCallback) |
|||
self.valueChanged.connect(self.changed) |
|||
|
|||
def updateFromCallback(self, newValue): |
|||
self.blockSignals(True) |
|||
self.setValue(newValue) |
|||
self.blockSignals(False) |
|||
|
|||
def changed(self): |
|||
api.playbackChangeSpeed(self.value()) |
|||
constantsAndConfigs.ccViewValue = self.value() #this sends a call back to all CC spinboxes in the whole program |
After Width: | Height: | Size: 11 KiB |
@ -0,0 +1,4 @@ |
|||
#!/bin/bash |
|||
#https://doc.qt.io/qt-5/resources.html |
|||
#Resources are kept up-to-date upstream. They are not part of the make and build process. |
|||
pyrcc5 -no-compress resources.qrc -o ../resources.py #put them into the gui directly. Engine does not need any translation or images. |
@ -0,0 +1,94 @@ |
|||
Euterpe Font - Ben Laenen, SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 |
|||
|
|||
----------------------------------------------------------- |
|||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 |
|||
----------------------------------------------------------- |
|||
|
|||
PREAMBLE |
|||
The goals of the Open Font License (OFL) are to stimulate worldwide |
|||
development of collaborative font projects, to support the font creation |
|||
efforts of academic and linguistic communities, and to provide a free and |
|||
open framework in which fonts may be shared and improved in partnership |
|||
with others. |
|||
|
|||
The OFL allows the licensed fonts to be used, studied, modified and |
|||
redistributed freely as long as they are not sold by themselves. The |
|||
fonts, including any derivative works, can be bundled, embedded, |
|||
redistributed and/or sold with any software provided that any reserved |
|||
names are not used by derivative works. The fonts and derivatives, |
|||
however, cannot be released under any other type of license. The |
|||
requirement for fonts to remain under this license does not apply |
|||
to any document created using the fonts or their derivatives. |
|||
|
|||
DEFINITIONS |
|||
"Font Software" refers to the set of files released by the Copyright |
|||
Holder(s) under this license and clearly marked as such. This may |
|||
include source files, build scripts and documentation. |
|||
|
|||
"Reserved Font Name" refers to any names specified as such after the |
|||
copyright statement(s). |
|||
|
|||
"Original Version" refers to the collection of Font Software components as |
|||
distributed by the Copyright Holder(s). |
|||
|
|||
"Modified Version" refers to any derivative made by adding to, deleting, |
|||
or substituting -- in part or in whole -- any of the components of the |
|||
Original Version, by changing formats or by porting the Font Software to a |
|||
new environment. |
|||
|
|||
"Author" refers to any designer, engineer, programmer, technical |
|||
writer or other person who contributed to the Font Software. |
|||
|
|||
PERMISSION & CONDITIONS |
|||
Permission is hereby granted, free of charge, to any person obtaining |
|||
a copy of the Font Software, to use, study, copy, merge, embed, modify, |
|||
redistribute, and sell modified and unmodified copies of the Font |
|||
Software, subject to the following conditions: |
|||
|
|||
1) Neither the Font Software nor any of its individual components, |
|||
in Original or Modified Versions, may be sold by itself. |
|||
|
|||
2) Original or Modified Versions of the Font Software may be bundled, |
|||
redistributed and/or sold with any software, provided that each copy |
|||
contains the above copyright notice and this license. These can be |
|||
included either as stand-alone text files, human-readable headers or |
|||
in the appropriate machine-readable metadata fields within text or |
|||
binary files as long as those fields can be easily viewed by the user. |
|||
|
|||
3) No Modified Version of the Font Software may use the Reserved Font |
|||
Name(s) unless explicit written permission is granted by the corresponding |
|||
Copyright Holder. This restriction only applies to the primary font name as |
|||
presented to the users. |
|||
|
|||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font |
|||
Software shall not be used to promote, endorse or advertise any |
|||
Modified Version, except to acknowledge the contribution(s) of the |
|||
Copyright Holder(s) and the Author(s) or with their explicit written |
|||
permission. |
|||
|
|||
5) The Font Software, modified or unmodified, in part or in whole, |
|||
must be distributed entirely under this license, and must not be |
|||
distributed under any other license. The requirement for fonts to |
|||
remain under this license does not apply to any document created |
|||
using the Font Software. |
|||
|
|||
TERMINATION |
|||
This license becomes null and void if any of the above conditions are |
|||
not met. |
|||
|
|||
DISCLAIMER |
|||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF |
|||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT |
|||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE |
|||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
|||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL |
|||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM |
|||
OTHER DEALINGS IN THE FONT SOFTWARE. |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
After Width: | Height: | Size: 942 B |
@ -0,0 +1,55 @@ |
|||
<!DOCTYPE RCC><RCC version="1.0"> |
|||
<qresource> |
|||
<file>euterpe.ttf</file> |
|||
<file>aboutlogo.png</file> |
|||
<file>icon.png</file> |
|||
<file>translations/de.qm</file> |
|||
|
|||
<file>svg/accidentalsDoublesharp.svg</file> |
|||
<file>svg/accidentalsFlatFlat.svg</file> |
|||
<file>svg/accidentalsFlat.svg</file> |
|||
<file>svg/accidentalsNatural.svg</file> |
|||
<file>svg/accidentalsSharp.svg</file> |
|||
<file>svg/blockEnd.svg</file> |
|||
<file>svg/clefAlto.svg</file> |
|||
<file>svg/clefBass_8.svg</file> |
|||
<file>svg/clefBass.svg</file> |
|||
<file>svg/clefPercussion.svg</file> |
|||
<file>svg/clefTreble^8.svg</file> |
|||
<file>svg/clefTreble_8.svg</file> |
|||
<file>svg/clefTreble.svg</file> |
|||
<file>svg/dot.svg</file> |
|||
<file>svg/flag128i.svg</file> |
|||
<file>svg/flag128.svg</file> |
|||
<file>svg/flag16i.svg</file> |
|||
<file>svg/flag16.svg</file> |
|||
<file>svg/flag32i.svg</file> |
|||
<file>svg/flag32.svg</file> |
|||
<file>svg/flag64i.svg</file> |
|||
<file>svg/flag64.svg</file> |
|||
<file>svg/flag8i.svg</file> |
|||
<file>svg/flag8.svg</file> |
|||
<file>svg/noteheadsBlack.svg</file> |
|||
<file>svg/noteheadsBrevis.svg</file> |
|||
<file>svg/noteheadsHalf.svg</file> |
|||
<file>svg/noteheadsLonga.svg</file> |
|||
<file>svg/noteheadsMaxima.svg</file> |
|||
<file>svg/noteheadsWhole.svg</file> |
|||
<file>svg/numbers.svg</file> |
|||
<file>svg/rest128.svg</file> |
|||
<file>svg/rest16.svg</file> |
|||
<file>svg/rest1.svg</file> |
|||
<file>svg/rest2.svg</file> |
|||
<file>svg/rest32.svg</file> |
|||
<file>svg/rest4.svg</file> |
|||
<file>svg/rest64.svg</file> |
|||
<file>svg/rest8.svg</file> |
|||
<file>svg/restBrevis.svg</file> |
|||
<file>svg/restLonga.svg</file> |
|||
<file>svg/restMaxima.svg</file> |
|||
<file>svg/scriptsStaccato.svg</file> |
|||
|
|||
</qresource> |
|||
</RCC> |
|||
|
|||
|
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 1009 B |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 8.8 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 911 B |
@ -0,0 +1,678 @@ |
|||
Laborejos Notation Glyphs are based on Lilypond - Lilypond Team, GPLv3, http://lilypond.org/ |
|||
|
|||
GNU GENERAL PUBLIC LICENSE |
|||
Version 3, 29 June 2007 |
|||
|
|||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> |
|||
Everyone is permitted to copy and distribute verbatim copies |
|||
of this license document, but changing it is not allowed. |
|||
|
|||
Preamble |
|||
|
|||
The GNU General Public License is a free, copyleft license for |
|||
software and other kinds of works. |
|||
|
|||
The licenses for most software and other practical works are designed |
|||
to take away your freedom to share and change the works. By contrast, |
|||
the GNU General Public License is intended to guarantee your freedom to |
|||
share and change all versions of a program--to make sure it remains free |
|||
software for all its users. We, the Free Software Foundation, use the |
|||
GNU General Public License for most of our software; it applies also to |
|||
any other work released this way by its authors. You can apply it to |
|||
your programs, too. |
|||
|
|||
When we speak of free software, we are referring to freedom, not |
|||
price. Our General Public Licenses are designed to make sure that you |
|||
have the freedom to distribute copies of free software (and charge for |
|||
them if you wish), that you receive source code or can get it if you |
|||
want it, that you can change the software or use pieces of it in new |
|||
free programs, and that you know you can do these things. |
|||
|
|||
To protect your rights, we need to prevent others from denying you |
|||
these rights or asking you to surrender the rights. Therefore, you have |
|||
certain responsibilities if you distribute copies of the software, or if |
|||
you modify it: responsibilities to respect the freedom of others. |
|||
|
|||
For example, if you distribute copies of such a program, whether |
|||
gratis or for a fee, you must pass on to the recipients the same |
|||
freedoms that you received. You must make sure that they, too, receive |
|||
or can get the source code. And you must show them these terms so they |
|||
know their rights. |
|||
|
|||
Developers that use the GNU GPL protect your rights with two steps: |
|||
(1) assert copyright on the software, and (2) offer you this License |
|||
giving you legal permission to copy, distribute and/or modify it. |
|||
|
|||
For the developers' and authors' protection, the GPL clearly explains |
|||
that there is no warranty for this free software. For both users' and |
|||
authors' sake, the GPL requires that modified versions be marked as |
|||
changed, so that their problems will not be attributed erroneously to |
|||
authors of previous versions. |
|||
|
|||
Some devices are designed to deny users access to install or run |
|||
modified versions of the software inside them, although the manufacturer |
|||
can do so. This is fundamentally incompatible with the aim of |
|||
protecting users' freedom to change the software. The systematic |
|||
pattern of such abuse occurs in the area of products for individuals to |
|||
use, which is precisely where it is most unacceptable. Therefore, we |
|||
have designed this version of the GPL to prohibit the practice for those |
|||
products. If such problems arise substantially in other domains, we |
|||
stand ready to extend this provision to those domains in future versions |
|||
of the GPL, as needed to protect the freedom of users. |
|||
|
|||
Finally, every program is threatened constantly by software patents. |
|||
States should not allow patents to restrict development and use of |
|||
software on general-purpose computers, but in those that do, we wish to |
|||
avoid the special danger that patents applied to a free program could |
|||
make it effectively proprietary. To prevent this, the GPL assures that |
|||
patents cannot be used to render the program non-free. |
|||
|
|||
The precise terms and conditions for copying, distribution and |
|||
modification follow. |
|||
|
|||
TERMS AND CONDITIONS |
|||
|
|||
0. Definitions. |
|||
|
|||
"This License" refers to version 3 of the GNU General Public License. |
|||
|
|||
"Copyright" also means copyright-like laws that apply to other kinds of |
|||
works, such as semiconductor masks. |
|||
|
|||
"The Program" refers to any copyrightable work licensed under this |
|||
License. Each licensee is addressed as "you". "Licensees" and |
|||
"recipients" may be individuals or organizations. |
|||
|
|||
To "modify" a work means to copy from or adapt all or part of the work |
|||
in a fashion requiring copyright permission, other than the making of an |
|||
exact copy. The resulting work is called a "modified version" of the |
|||
earlier work or a work "based on" the earlier work. |
|||
|
|||
A "covered work" means either the unmodified Program or a work based |
|||
on the Program. |
|||
|
|||
To "propagate" a work means to do anything with it that, without |
|||
permission, would make you directly or secondarily liable for |
|||
infringement under applicable copyright law, except executing it on a |
|||
computer or modifying a private copy. Propagation includes copying, |
|||
distribution (with or without modification), making available to the |
|||
public, and in some countries other activities as well. |
|||
|
|||
To "convey" a work means any kind of propagation that enables other |
|||
parties to make or receive copies. Mere interaction with a user through |
|||
a computer network, with no transfer of a copy, is not conveying. |
|||
|
|||
An interactive user interface displays "Appropriate Legal Notices" |
|||
to the extent that it includes a convenient and prominently visible |
|||
feature that (1) displays an appropriate copyright notice, and (2) |
|||
tells the user that there is no warranty for the work (except to the |
|||
extent that warranties are provided), that licensees may convey the |
|||
work under this License, and how to view a copy of this License. If |
|||
the interface presents a list of user commands or options, such as a |
|||
menu, a prominent item in the list meets this criterion. |
|||
|
|||
1. Source Code. |
|||
|
|||
The "source code" for a work means the preferred form of the work |
|||
for making modifications to it. "Object code" means any non-source |
|||
form of a work. |
|||
|
|||
A "Standard Interface" means an interface that either is an official |
|||
standard defined by a recognized standards body, or, in the case of |
|||
interfaces specified for a particular programming language, one that |
|||
is widely used among developers working in that language. |
|||
|
|||
The "System Libraries" of an executable work include anything, other |
|||
than the work as a whole, that (a) is included in the normal form of |
|||
packaging a Major Component, but which is not part of that Major |
|||
Component, and (b) serves only to enable use of the work with that |
|||
Major Component, or to implement a Standard Interface for which an |
|||
implementation is available to the public in source code form. A |
|||
"Major Component", in this context, means a major essential component |
|||
(kernel, window system, and so on) of the specific operating system |
|||
(if any) on which the executable work runs, or a compiler used to |
|||
produce the work, or an object code interpreter used to run it. |
|||
|
|||
The "Corresponding Source" for a work in object code form means all |
|||
the source code needed to generate, install, and (for an executable |
|||
work) run the object code and to modify the work, including scripts to |
|||
control those activities. However, it does not include the work's |
|||
System Libraries, or general-purpose tools or generally available free |
|||
programs which are used unmodified in performing those activities but |
|||
which are not part of the work. For example, Corresponding Source |
|||
includes interface definition files associated with source files for |
|||
the work, and the source code for shared libraries and dynamically |
|||
linked subprograms that the work is specifically designed to require, |
|||
such as by intimate data communication or control flow between those |
|||
subprograms and other parts of the work. |
|||
|
|||
The Corresponding Source need not include anything that users |
|||
can regenerate automatically from other parts of the Corresponding |
|||
Source. |
|||
|
|||
The Corresponding Source for a work in source code form is that |
|||
same work. |
|||
|
|||
2. Basic Permissions. |
|||
|
|||
All rights granted under this License are granted for the term of |
|||
copyright on the Program, and are irrevocable provided the stated |
|||
conditions are met. This License explicitly affirms your unlimited |
|||
permission to run the unmodified Program. The output from running a |
|||
covered work is covered by this License only if the output, given its |
|||
content, constitutes a covered work. This License acknowledges your |
|||
rights of fair use or other equivalent, as provided by copyright law. |
|||
|
|||
You may make, run and propagate covered works that you do not |
|||
convey, without conditions so long as your license otherwise remains |
|||
in force. You may convey covered works to others for the sole purpose |
|||
of having them make modifications exclusively for you, or provide you |
|||
with facilities for running those works, provided that you comply with |
|||
the terms of this License in conveying all material for which you do |
|||
not control copyright. Those thus making or running the covered works |
|||
for you must do so exclusively on your behalf, under your direction |
|||
and control, on terms that prohibit them from making any copies of |
|||
your copyrighted material outside their relationship with you. |
|||
|
|||
Conveying under any other circumstances is permitted solely under |
|||
the conditions stated below. Sublicensing is not allowed; section 10 |
|||
makes it unnecessary. |
|||
|
|||
3. Protecting Users' Legal Rights From Anti-Circumvention Law. |
|||
|
|||
No covered work shall be deemed part of an effective technological |
|||
measure under any applicable law fulfilling obligations under article |
|||
11 of the WIPO copyright treaty adopted on 20 December 1996, or |
|||
similar laws prohibiting or restricting circumvention of such |
|||
measures. |
|||
|
|||
When you convey a covered work, you waive any legal power to forbid |
|||
circumvention of technological measures to the extent such circumvention |
|||
is effected by exercising rights under this License with respect to |
|||
the covered work, and you disclaim any intention to limit operation or |
|||
modification of the work as a means of enforcing, against the work's |
|||
users, your or third parties' legal rights to forbid circumvention of |
|||
technological measures. |
|||
|
|||
4. Conveying Verbatim Copies. |
|||
|
|||
You may convey verbatim copies of the Program's source code as you |
|||
receive it, in any medium, provided that you conspicuously and |
|||
appropriately publish on each copy an appropriate copyright notice; |
|||
keep intact all notices stating that this License and any |
|||
non-permissive terms added in accord with section 7 apply to the code; |
|||
keep intact all notices of the absence of any warranty; and give all |
|||
recipients a copy of this License along with the Program. |
|||
|
|||
You may charge any price or no price for each copy that you convey, |
|||
and you may offer support or warranty protection for a fee. |
|||
|
|||
5. Conveying Modified Source Versions. |
|||
|
|||
You may convey a work based on the Program, or the modifications to |
|||
produce it from the Program, in the form of source code under the |
|||
terms of section 4, provided that you also meet all of these conditions: |
|||
|
|||
a) The work must carry prominent notices stating that you modified |
|||
it, and giving a relevant date. |
|||
|
|||
b) The work must carry prominent notices stating that it is |
|||
released under this License and any conditions added under section |
|||
7. This requirement modifies the requirement in section 4 to |
|||
"keep intact all notices". |
|||
|
|||
c) You must license the entire work, as a whole, under this |
|||
License to anyone who comes into possession of a copy. This |
|||
License will therefore apply, along with any applicable section 7 |
|||
additional terms, to the whole of the work, and all its parts, |
|||
regardless of how they are packaged. This License gives no |
|||
permission to license the work in any other way, but it does not |
|||
invalidate such permission if you have separately received it. |
|||
|
|||
d) If the work has interactive user interfaces, each must display |
|||
Appropriate Legal Notices; however, if the Program has interactive |
|||
interfaces that do not display Appropriate Legal Notices, your |
|||
work need not make them do so. |
|||
|
|||
A compilation of a covered work with other separate and independent |
|||
works, which are not by their nature extensions of the covered work, |
|||
and which are not combined with it such as to form a larger program, |
|||
in or on a volume of a storage or distribution medium, is called an |
|||
"aggregate" if the compilation and its resulting copyright are not |
|||
used to limit the access or legal rights of the compilation's users |
|||
beyond what the individual works permit. Inclusion of a covered work |
|||
in an aggregate does not cause this License to apply to the other |
|||
parts of the aggregate. |
|||
|
|||
6. Conveying Non-Source Forms. |
|||
|
|||
You may convey a covered work in object code form under the terms |
|||
of sections 4 and 5, provided that you also convey the |
|||
machine-readable Corresponding Source under the terms of this License, |
|||
in one of these ways: |
|||
|
|||
a) Convey the object code in, or embodied in, a physical product |
|||
(including a physical distribution medium), accompanied by the |
|||
Corresponding Source fixed on a durable physical medium |
|||
customarily used for software interchange. |
|||
|
|||
b) Convey the object code in, or embodied in, a physical product |
|||
(including a physical distribution medium), accompanied by a |
|||
written offer, valid for at least three years and valid for as |
|||
long as you offer spare parts or customer support for that product |
|||
model, to give anyone who possesses the object code either (1) a |
|||
copy of the Corresponding Source for all the software in the |
|||
product that is covered by this License, on a durable physical |
|||
medium customarily used for software interchange, for a price no |
|||
more than your reasonable cost of physically performing this |
|||
conveying of source, or (2) access to copy the |
|||
Corresponding Source from a network server at no charge. |
|||
|
|||
c) Convey individual copies of the object code with a copy of the |
|||
written offer to provide the Corresponding Source. This |
|||
alternative is allowed only occasionally and noncommercially, and |
|||
only if you received the object code with such an offer, in accord |
|||
with subsection 6b. |
|||
|
|||
d) Convey the object code by offering access from a designated |
|||
place (gratis or for a charge), and offer equivalent access to the |
|||
Corresponding Source in the same way through the same place at no |
|||
further charge. You need not require recipients to copy the |
|||
Corresponding Source along with the object code. If the place to |
|||
copy the object code is a network server, the Corresponding Source |
|||
may be on a different server (operated by you or a third party) |
|||
that supports equivalent copying facilities, provided you maintain |
|||
clear directions next to the object code saying where to find the |
|||
Corresponding Source. Regardless of what server hosts the |
|||
Corresponding Source, you remain obligated to ensure that it is |
|||
available for as long as needed to satisfy these requirements. |
|||
|
|||
e) Convey the object code using peer-to-peer transmission, provided |
|||
you inform other peers where the object code and Corresponding |
|||
Source of the work are being offered to the general public at no |
|||
charge under subsection 6d. |
|||
|
|||
A separable portion of the object code, whose source code is excluded |
|||
from the Corresponding Source as a System Library, need not be |
|||
included in conveying the object code work. |
|||
|
|||
A "User Product" is either (1) a "consumer product", which means any |
|||
tangible personal property which is normally used for personal, family, |
|||
or household purposes, or (2) anything designed or sold for incorporation |
|||
into a dwelling. In determining whether a product is a consumer product, |
|||
doubtful cases shall be resolved in favor of coverage. For a particular |
|||
product received by a particular user, "normally used" refers to a |
|||
typical or common use of that class of product, regardless of the status |
|||
of the particular user or of the way in which the particular user |
|||
actually uses, or expects or is expected to use, the product. A product |
|||
is a consumer product regardless of whether the product has substantial |
|||
commercial, industrial or non-consumer uses, unless such uses represent |
|||
the only significant mode of use of the product. |
|||
|
|||
"Installation Information" for a User Product means any methods, |
|||
procedures, authorization keys, or other information required to install |
|||
and execute modified versions of a covered work in that User Product from |
|||
a modified version of its Corresponding Source. The information must |
|||
suffice to ensure that the continued functioning of the modified object |
|||
code is in no case prevented or interfered with solely because |
|||
modification has been made. |
|||
|
|||
If you convey an object code work under this section in, or with, or |
|||
specifically for use in, a User Product, and the conveying occurs as |
|||
part of a transaction in which the right of possession and use of the |
|||
User Product is transferred to the recipient in perpetuity or for a |
|||
fixed term (regardless of how the transaction is characterized), the |
|||
Corresponding Source conveyed under this section must be accompanied |
|||
by the Installation Information. But this requirement does not apply |
|||
if neither you nor any third party retains the ability to install |
|||
modified object code on the User Product (for example, the work has |
|||
been installed in ROM). |
|||
|
|||
The requirement to provide Installation Information does not include a |
|||
requirement to continue to provide support service, warranty, or updates |
|||
for a work that has been modified or installed by the recipient, or for |
|||
the User Product in which it has been modified or installed. Access to a |
|||
network may be denied when the modification itself materially and |
|||
adversely affects the operation of the network or violates the rules and |
|||
protocols for communication across the network. |
|||
|
|||
Corresponding Source conveyed, and Installation Information provided, |
|||
in accord with this section must be in a format that is publicly |
|||
documented (and with an implementation available to the public in |
|||
source code form), and must require no special password or key for |
|||
unpacking, reading or copying. |
|||
|
|||
7. Additional Terms. |
|||
|
|||
"Additional permissions" are terms that supplement the terms of this |
|||
License by making exceptions from one or more of its conditions. |
|||
Additional permissions that are applicable to the entire Program shall |
|||
be treated as though they were included in this License, to the extent |
|||
that they are valid under applicable law. If additional permissions |
|||
apply only to part of the Program, that part may be used separately |
|||
under those permissions, but the entire Program remains governed by |
|||
this License without regard to the additional permissions. |
|||
|
|||
When you convey a copy of a covered work, you may at your option |
|||
remove any additional permissions from that copy, or from any part of |
|||
it. (Additional permissions may be written to require their own |
|||
removal in certain cases when you modify the work.) You may place |
|||
additional permissions on material, added by you to a covered work, |
|||
for which you have or can give appropriate copyright permission. |
|||
|
|||
Notwithstanding any other provision of this License, for material you |
|||
add to a covered work, you may (if authorized by the copyright holders of |
|||
that material) supplement the terms of this License with terms: |
|||
|
|||
a) Disclaiming warranty or limiting liability differently from the |
|||
terms of sections 15 and 16 of this License; or |
|||
|
|||
b) Requiring preservation of specified reasonable legal notices or |
|||
author attributions in that material or in the Appropriate Legal |
|||
Notices displayed by works containing it; or |
|||
|
|||
c) Prohibiting misrepresentation of the origin of that material, or |
|||
requiring that modified versions of such material be marked in |
|||
reasonable ways as different from the original version; or |
|||
|
|||
d) Limiting the use for publicity purposes of names of licensors or |
|||
authors of the material; or |
|||
|
|||
e) Declining to grant rights under trademark law for use of some |
|||
trade names, trademarks, or service marks; or |
|||
|
|||
f) Requiring indemnification of licensors and authors of that |
|||
material by anyone who conveys the material (or modified versions of |
|||
it) with contractual assumptions of liability to the recipient, for |
|||
any liability that these contractual assumptions directly impose on |
|||
those licensors and authors. |
|||
|
|||
All other non-permissive additional terms are considered "further |
|||
restrictions" within the meaning of section 10. If the Program as you |
|||
received it, or any part of it, contains a notice stating that it is |
|||
governed by this License along with a term that is a further |
|||
restriction, you may remove that term. If a license document contains |
|||
a further restriction but permits relicensing or conveying under this |
|||
License, you may add to a covered work material governed by the terms |
|||
of that license document, provided that the further restriction does |
|||
not survive such relicensing or conveying. |
|||
|
|||
If you add terms to a covered work in accord with this section, you |
|||
must place, in the relevant source files, a statement of the |
|||
additional terms that apply to those files, or a notice indicating |
|||
where to find the applicable terms. |
|||
|
|||
Additional terms, permissive or non-permissive, may be stated in the |
|||
form of a separately written license, or stated as exceptions; |
|||
the above requirements apply either way. |
|||
|
|||
8. Termination. |
|||
|
|||
You may not propagate or modify a covered work except as expressly |
|||
provided under this License. Any attempt otherwise to propagate or |
|||
modify it is void, and will automatically terminate your rights under |
|||
this License (including any patent licenses granted under the third |
|||
paragraph of section 11). |
|||
|
|||
However, if you cease all violation of this License, then your |
|||
license from a particular copyright holder is reinstated (a) |
|||
provisionally, unless and until the copyright holder explicitly and |
|||
finally terminates your license, and (b) permanently, if the copyright |
|||
holder fails to notify you of the violation by some reasonable means |
|||
prior to 60 days after the cessation. |
|||
|
|||
Moreover, your license from a particular copyright holder is |
|||
reinstated permanently if the copyright holder notifies you of the |
|||
violation by some reasonable means, this is the first time you have |
|||
received notice of violation of this License (for any work) from that |
|||
copyright holder, and you cure the violation prior to 30 days after |
|||
your receipt of the notice. |
|||
|
|||
Termination of your rights under this section does not terminate the |
|||
licenses of parties who have received copies or rights from you under |
|||
this License. If your rights have been terminated and not permanently |
|||
reinstated, you do not qualify to receive new licenses for the same |
|||
material under section 10. |
|||
|
|||
9. Acceptance Not Required for Having Copies. |
|||
|
|||
You are not required to accept this License in order to receive or |
|||
run a copy of the Program. Ancillary propagation of a covered work |
|||
occurring solely as a consequence of using peer-to-peer transmission |
|||
to receive a copy likewise does not require acceptance. However, |
|||
nothing other than this License grants you permission to propagate or |
|||
modify any covered work. These actions infringe copyright if you do |
|||
not accept this License. Therefore, by modifying or propagating a |
|||
covered work, you indicate your acceptance of this License to do so. |
|||
|
|||
10. Automatic Licensing of Downstream Recipients. |
|||
|
|||
Each time you convey a covered work, the recipient automatically |
|||
receives a license from the original licensors, to run, modify and |
|||
propagate that work, subject to this License. You are not responsible |
|||
for enforcing compliance by third parties with this License. |
|||
|
|||
An "entity transaction" is a transaction transferring control of an |
|||
organization, or substantially all assets of one, or subdividing an |
|||
organization, or merging organizations. If propagation of a covered |
|||
work results from an entity transaction, each party to that |
|||
transaction who receives a copy of the work also receives whatever |
|||
licenses to the work the party's predecessor in interest had or could |
|||
give under the previous paragraph, plus a right to possession of the |
|||
Corresponding Source of the work from the predecessor in interest, if |
|||
the predecessor has it or can get it with reasonable efforts. |
|||
|
|||
You may not impose any further restrictions on the exercise of the |
|||
rights granted or affirmed under this License. For example, you may |
|||
not impose a license fee, royalty, or other charge for exercise of |
|||
rights granted under this License, and you may not initiate litigation |
|||
(including a cross-claim or counterclaim in a lawsuit) alleging that |
|||
any patent claim is infringed by making, using, selling, offering for |
|||
sale, or importing the Program or any portion of it. |
|||
|
|||
11. Patents. |
|||
|
|||
A "contributor" is a copyright holder who authorizes use under this |
|||
License of the Program or a work on which the Program is based. The |
|||
work thus licensed is called the contributor's "contributor version". |
|||
|
|||
A contributor's "essential patent claims" are all patent claims |
|||
owned or controlled by the contributor, whether already acquired or |
|||
hereafter acquired, that would be infringed by some manner, permitted |
|||
by this License, of making, using, or selling its contributor version, |
|||
but do not include claims that would be infringed only as a |
|||
consequence of further modification of the contributor version. For |
|||
purposes of this definition, "control" includes the right to grant |
|||
patent sublicenses in a manner consistent with the requirements of |
|||
this License. |
|||
|
|||
Each contributor grants you a non-exclusive, worldwide, royalty-free |
|||
patent license under the contributor's essential patent claims, to |
|||
make, use, sell, offer for sale, import and otherwise run, modify and |
|||
propagate the contents of its contributor version. |
|||
|
|||
In the following three paragraphs, a "patent license" is any express |
|||
agreement or commitment, however denominated, not to enforce a patent |
|||
(such as an express permission to practice a patent or covenant not to |
|||
sue for patent infringement). To "grant" such a patent license to a |
|||
party means to make such an agreement or commitment not to enforce a |
|||
patent against the party. |
|||
|
|||
If you convey a covered work, knowingly relying on a patent license, |
|||
and the Corresponding Source of the work is not available for anyone |
|||
to copy, free of charge and under the terms of this License, through a |
|||
publicly available network server or other readily accessible means, |
|||
then you must either (1) cause the Corresponding Source to be so |
|||
available, or (2) arrange to deprive yourself of the benefit of the |
|||
patent license for this particular work, or (3) arrange, in a manner |
|||
consistent with the requirements of this License, to extend the patent |
|||
license to downstream recipients. "Knowingly relying" means you have |
|||
actual knowledge that, but for the patent license, your conveying the |
|||
covered work in a country, or your recipient's use of the covered work |
|||
in a country, would infringe one or more identifiable patents in that |
|||
country that you have reason to believe are valid. |
|||
|
|||
If, pursuant to or in connection with a single transaction or |
|||
arrangement, you convey, or propagate by procuring conveyance of, a |
|||
covered work, and grant a patent license to some of the parties |
|||
receiving the covered work authorizing them to use, propagate, modify |
|||
or convey a specific copy of the covered work, then the patent license |
|||
you grant is automatically extended to all recipients of the covered |
|||
work and works based on it. |
|||
|
|||
A patent license is "discriminatory" if it does not include within |
|||
the scope of its coverage, prohibits the exercise of, or is |
|||
conditioned on the non-exercise of one or more of the rights that are |
|||
specifically granted under this License. You may not convey a covered |
|||
work if you are a party to an arrangement with a third party that is |
|||
in the business of distributing software, under which you make payment |
|||
to the third party based on the extent of your activity of conveying |
|||
the work, and under which the third party grants, to any of the |
|||
parties who would receive the covered work from you, a discriminatory |
|||
patent license (a) in connection with copies of the covered work |
|||
conveyed by you (or copies made from those copies), or (b) primarily |
|||
for and in connection with specific products or compilations that |
|||
contain the covered work, unless you entered into that arrangement, |
|||
or that patent license was granted, prior to 28 March 2007. |
|||
|
|||
Nothing in this License shall be construed as excluding or limiting |
|||
any implied license or other defenses to infringement that may |
|||
otherwise be available to you under applicable patent law. |
|||
|
|||
12. No Surrender of Others' Freedom. |
|||
|
|||
If conditions are imposed on you (whether by court order, agreement or |
|||
otherwise) that contradict the conditions of this License, they do not |
|||
excuse you from the conditions of this License. If you cannot convey a |
|||
covered work so as to satisfy simultaneously your obligations under this |
|||
License and any other pertinent obligations, then as a consequence you may |
|||
not convey it at all. For example, if you agree to terms that obligate you |
|||
to collect a royalty for further conveying from those to whom you convey |
|||
the Program, the only way you could satisfy both those terms and this |
|||
License would be to refrain entirely from conveying the Program. |
|||
|
|||
13. Use with the GNU Affero General Public License. |
|||
|
|||
Notwithstanding any other provision of this License, you have |
|||
permission to link or combine any covered work with a work licensed |
|||
under version 3 of the GNU Affero General Public License into a single |
|||
combined work, and to convey the resulting work. The terms of this |
|||
License will continue to apply to the part which is the covered work, |
|||
but the special requirements of the GNU Affero General Public License, |
|||
section 13, concerning interaction through a network will apply to the |
|||
combination as such. |
|||
|
|||
14. Revised Versions of this License. |
|||
|
|||
The Free Software Foundation may publish revised and/or new versions of |
|||
the GNU General Public License from time to time. Such new versions will |
|||
be similar in spirit to the present version, but may differ in detail to |
|||
address new problems or concerns. |
|||
|
|||
Each version is given a distinguishing version number. If the |
|||
Program specifies that a certain numbered version of the GNU General |
|||
Public License "or any later version" applies to it, you have the |
|||
option of following the terms and conditions either of that numbered |
|||
version or of any later version published by the Free Software |
|||
Foundation. If the Program does not specify a version number of the |
|||
GNU General Public License, you may choose any version ever published |
|||
by the Free Software Foundation. |
|||
|
|||
If the Program specifies that a proxy can decide which future |
|||
versions of the GNU General Public License can be used, that proxy's |
|||
public statement of acceptance of a version permanently authorizes you |
|||
to choose that version for the Program. |
|||
|
|||
Later license versions may give you additional or different |
|||
permissions. However, no additional obligations are imposed on any |
|||
author or copyright holder as a result of your choosing to follow a |
|||
later version. |
|||
|
|||
15. Disclaimer of Warranty. |
|||
|
|||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY |
|||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT |
|||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY |
|||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, |
|||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM |
|||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF |
|||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION. |
|||
|
|||
16. Limitation of Liability. |
|||
|
|||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |
|||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS |
|||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY |
|||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE |
|||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF |
|||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD |
|||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), |
|||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF |
|||
SUCH DAMAGES. |
|||
|
|||
17. Interpretation of Sections 15 and 16. |
|||
|
|||
If the disclaimer of warranty and limitation of liability provided |
|||
above cannot be given local legal effect according to their terms, |
|||
reviewing courts shall apply local law that most closely approximates |
|||
an absolute waiver of all civil liability in connection with the |
|||
Program, unless a warranty or assumption of liability accompanies a |
|||
copy of the Program in return for a fee. |
|||
|
|||
END OF TERMS AND CONDITIONS |
|||
|
|||
How to Apply These Terms to Your New Programs |
|||
|
|||
If you develop a new program, and you want it to be of the greatest |
|||
possible use to the public, the best way to achieve this is to make it |
|||
free software which everyone can redistribute and change under these terms. |
|||
|
|||
To do so, attach the following notices to the program. It is safest |
|||
to attach them to the start of each source file to most effectively |
|||
state the exclusion of warranty; and each file should have at least |
|||
the "copyright" line and a pointer to where the full notice is found. |
|||
|
|||
<one line to give the program's name and a brief idea of what it does.> |
|||
Copyright (C) <year> <name of author> |
|||
|
|||
This program is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
|
|||
Also add information on how to contact you by electronic and paper mail. |
|||
|
|||
If the program does terminal interaction, make it output a short |
|||
notice like this when it starts in an interactive mode: |
|||
|
|||
<program> Copyright (C) <year> <name of author> |
|||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. |
|||
This is free software, and you are welcome to redistribute it |
|||
under certain conditions; type `show c' for details. |
|||
|
|||
The hypothetical commands `show w' and `show c' should show the appropriate |
|||
parts of the General Public License. Of course, your program's commands |
|||
might be different; for a GUI interface, you would use an "about box". |
|||
|
|||
You should also get your employer (if you work as a programmer) or school, |
|||
if any, to sign a "copyright disclaimer" for the program, if necessary. |
|||
For more information on this, and how to apply and follow the GNU GPL, see |
|||
<http://www.gnu.org/licenses/>. |
|||
|
|||
The GNU General Public License does not permit incorporating your program |
|||
into proprietary programs. If your program is a subroutine library, you |
|||
may consider it more useful to permit linking proprietary applications with |
|||
the library. If this is what you want to do, use the GNU Lesser General |
|||
Public License instead of this License. But first, please read |
|||
<http://www.gnu.org/philosophy/why-not-lgpl.html>. |
|||
|
|||
|
@ -0,0 +1,2 @@ |
|||
SOURCES = ../../mainwindow.py ../../designer/mainwindow.py |
|||
TRANSLATIONS = de.ts |
@ -0,0 +1,11 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<!DOCTYPE TS><TS version="2.0" language="de" sourcelanguage=""> |
|||
<context> |
|||
<name>About</name> |
|||
<message> |
|||
<location filename="../../mainwindow.py" line="57"/> |
|||
<source>This is an example application. Extend it to your liking. Start by editing config.py</source> |
|||
<translation>Willkommen im Beispielprogramm. Erstellen Sie neue Programme auf dieser Grundlage wie es Ihnen gefällt. Ein guter Einstieg wäre config.py</translation> |
|||
</message> |
|||
</context> |
|||
</TS> |
@ -0,0 +1,5 @@ |
|||
#!/bin/sh |
|||
|
|||
set -e |
|||
pylupdate5 config.pro |
|||
echo "linguist-qt5 de.ts" |