##// END OF EJS Templates
Added lppmonplot and QCustomPlot sources to wfdisplay
leroy -
r41:78506afea7a4 default
parent child
Show More
This diff has been collapsed as it changes many lines, (674 lines changed) Show them Hide them
@@ -0,0 +1,674
1 GNU GENERAL PUBLIC LICENSE
2 Version 3, 29 June 2007
3
4 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5 Everyone is permitted to copy and distribute verbatim copies
6 of this license document, but changing it is not allowed.
7
8 Preamble
9
10 The GNU General Public License is a free, copyleft license for
11 software and other kinds of works.
12
13 The licenses for most software and other practical works are designed
14 to take away your freedom to share and change the works. By contrast,
15 the GNU General Public License is intended to guarantee your freedom to
16 share and change all versions of a program--to make sure it remains free
17 software for all its users. We, the Free Software Foundation, use the
18 GNU General Public License for most of our software; it applies also to
19 any other work released this way by its authors. You can apply it to
20 your programs, too.
21
22 When we speak of free software, we are referring to freedom, not
23 price. Our General Public Licenses are designed to make sure that you
24 have the freedom to distribute copies of free software (and charge for
25 them if you wish), that you receive source code or can get it if you
26 want it, that you can change the software or use pieces of it in new
27 free programs, and that you know you can do these things.
28
29 To protect your rights, we need to prevent others from denying you
30 these rights or asking you to surrender the rights. Therefore, you have
31 certain responsibilities if you distribute copies of the software, or if
32 you modify it: responsibilities to respect the freedom of others.
33
34 For example, if you distribute copies of such a program, whether
35 gratis or for a fee, you must pass on to the recipients the same
36 freedoms that you received. You must make sure that they, too, receive
37 or can get the source code. And you must show them these terms so they
38 know their rights.
39
40 Developers that use the GNU GPL protect your rights with two steps:
41 (1) assert copyright on the software, and (2) offer you this License
42 giving you legal permission to copy, distribute and/or modify it.
43
44 For the developers' and authors' protection, the GPL clearly explains
45 that there is no warranty for this free software. For both users' and
46 authors' sake, the GPL requires that modified versions be marked as
47 changed, so that their problems will not be attributed erroneously to
48 authors of previous versions.
49
50 Some devices are designed to deny users access to install or run
51 modified versions of the software inside them, although the manufacturer
52 can do so. This is fundamentally incompatible with the aim of
53 protecting users' freedom to change the software. The systematic
54 pattern of such abuse occurs in the area of products for individuals to
55 use, which is precisely where it is most unacceptable. Therefore, we
56 have designed this version of the GPL to prohibit the practice for those
57 products. If such problems arise substantially in other domains, we
58 stand ready to extend this provision to those domains in future versions
59 of the GPL, as needed to protect the freedom of users.
60
61 Finally, every program is threatened constantly by software patents.
62 States should not allow patents to restrict development and use of
63 software on general-purpose computers, but in those that do, we wish to
64 avoid the special danger that patents applied to a free program could
65 make it effectively proprietary. To prevent this, the GPL assures that
66 patents cannot be used to render the program non-free.
67
68 The precise terms and conditions for copying, distribution and
69 modification follow.
70
71 TERMS AND CONDITIONS
72
73 0. Definitions.
74
75 "This License" refers to version 3 of the GNU General Public License.
76
77 "Copyright" also means copyright-like laws that apply to other kinds of
78 works, such as semiconductor masks.
79
80 "The Program" refers to any copyrightable work licensed under this
81 License. Each licensee is addressed as "you". "Licensees" and
82 "recipients" may be individuals or organizations.
83
84 To "modify" a work means to copy from or adapt all or part of the work
85 in a fashion requiring copyright permission, other than the making of an
86 exact copy. The resulting work is called a "modified version" of the
87 earlier work or a work "based on" the earlier work.
88
89 A "covered work" means either the unmodified Program or a work based
90 on the Program.
91
92 To "propagate" a work means to do anything with it that, without
93 permission, would make you directly or secondarily liable for
94 infringement under applicable copyright law, except executing it on a
95 computer or modifying a private copy. Propagation includes copying,
96 distribution (with or without modification), making available to the
97 public, and in some countries other activities as well.
98
99 To "convey" a work means any kind of propagation that enables other
100 parties to make or receive copies. Mere interaction with a user through
101 a computer network, with no transfer of a copy, is not conveying.
102
103 An interactive user interface displays "Appropriate Legal Notices"
104 to the extent that it includes a convenient and prominently visible
105 feature that (1) displays an appropriate copyright notice, and (2)
106 tells the user that there is no warranty for the work (except to the
107 extent that warranties are provided), that licensees may convey the
108 work under this License, and how to view a copy of this License. If
109 the interface presents a list of user commands or options, such as a
110 menu, a prominent item in the list meets this criterion.
111
112 1. Source Code.
113
114 The "source code" for a work means the preferred form of the work
115 for making modifications to it. "Object code" means any non-source
116 form of a work.
117
118 A "Standard Interface" means an interface that either is an official
119 standard defined by a recognized standards body, or, in the case of
120 interfaces specified for a particular programming language, one that
121 is widely used among developers working in that language.
122
123 The "System Libraries" of an executable work include anything, other
124 than the work as a whole, that (a) is included in the normal form of
125 packaging a Major Component, but which is not part of that Major
126 Component, and (b) serves only to enable use of the work with that
127 Major Component, or to implement a Standard Interface for which an
128 implementation is available to the public in source code form. A
129 "Major Component", in this context, means a major essential component
130 (kernel, window system, and so on) of the specific operating system
131 (if any) on which the executable work runs, or a compiler used to
132 produce the work, or an object code interpreter used to run it.
133
134 The "Corresponding Source" for a work in object code form means all
135 the source code needed to generate, install, and (for an executable
136 work) run the object code and to modify the work, including scripts to
137 control those activities. However, it does not include the work's
138 System Libraries, or general-purpose tools or generally available free
139 programs which are used unmodified in performing those activities but
140 which are not part of the work. For example, Corresponding Source
141 includes interface definition files associated with source files for
142 the work, and the source code for shared libraries and dynamically
143 linked subprograms that the work is specifically designed to require,
144 such as by intimate data communication or control flow between those
145 subprograms and other parts of the work.
146
147 The Corresponding Source need not include anything that users
148 can regenerate automatically from other parts of the Corresponding
149 Source.
150
151 The Corresponding Source for a work in source code form is that
152 same work.
153
154 2. Basic Permissions.
155
156 All rights granted under this License are granted for the term of
157 copyright on the Program, and are irrevocable provided the stated
158 conditions are met. This License explicitly affirms your unlimited
159 permission to run the unmodified Program. The output from running a
160 covered work is covered by this License only if the output, given its
161 content, constitutes a covered work. This License acknowledges your
162 rights of fair use or other equivalent, as provided by copyright law.
163
164 You may make, run and propagate covered works that you do not
165 convey, without conditions so long as your license otherwise remains
166 in force. You may convey covered works to others for the sole purpose
167 of having them make modifications exclusively for you, or provide you
168 with facilities for running those works, provided that you comply with
169 the terms of this License in conveying all material for which you do
170 not control copyright. Those thus making or running the covered works
171 for you must do so exclusively on your behalf, under your direction
172 and control, on terms that prohibit them from making any copies of
173 your copyrighted material outside their relationship with you.
174
175 Conveying under any other circumstances is permitted solely under
176 the conditions stated below. Sublicensing is not allowed; section 10
177 makes it unnecessary.
178
179 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180
181 No covered work shall be deemed part of an effective technological
182 measure under any applicable law fulfilling obligations under article
183 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 similar laws prohibiting or restricting circumvention of such
185 measures.
186
187 When you convey a covered work, you waive any legal power to forbid
188 circumvention of technological measures to the extent such circumvention
189 is effected by exercising rights under this License with respect to
190 the covered work, and you disclaim any intention to limit operation or
191 modification of the work as a means of enforcing, against the work's
192 users, your or third parties' legal rights to forbid circumvention of
193 technological measures.
194
195 4. Conveying Verbatim Copies.
196
197 You may convey verbatim copies of the Program's source code as you
198 receive it, in any medium, provided that you conspicuously and
199 appropriately publish on each copy an appropriate copyright notice;
200 keep intact all notices stating that this License and any
201 non-permissive terms added in accord with section 7 apply to the code;
202 keep intact all notices of the absence of any warranty; and give all
203 recipients a copy of this License along with the Program.
204
205 You may charge any price or no price for each copy that you convey,
206 and you may offer support or warranty protection for a fee.
207
208 5. Conveying Modified Source Versions.
209
210 You may convey a work based on the Program, or the modifications to
211 produce it from the Program, in the form of source code under the
212 terms of section 4, provided that you also meet all of these conditions:
213
214 a) The work must carry prominent notices stating that you modified
215 it, and giving a relevant date.
216
217 b) The work must carry prominent notices stating that it is
218 released under this License and any conditions added under section
219 7. This requirement modifies the requirement in section 4 to
220 "keep intact all notices".
221
222 c) You must license the entire work, as a whole, under this
223 License to anyone who comes into possession of a copy. This
224 License will therefore apply, along with any applicable section 7
225 additional terms, to the whole of the work, and all its parts,
226 regardless of how they are packaged. This License gives no
227 permission to license the work in any other way, but it does not
228 invalidate such permission if you have separately received it.
229
230 d) If the work has interactive user interfaces, each must display
231 Appropriate Legal Notices; however, if the Program has interactive
232 interfaces that do not display Appropriate Legal Notices, your
233 work need not make them do so.
234
235 A compilation of a covered work with other separate and independent
236 works, which are not by their nature extensions of the covered work,
237 and which are not combined with it such as to form a larger program,
238 in or on a volume of a storage or distribution medium, is called an
239 "aggregate" if the compilation and its resulting copyright are not
240 used to limit the access or legal rights of the compilation's users
241 beyond what the individual works permit. Inclusion of a covered work
242 in an aggregate does not cause this License to apply to the other
243 parts of the aggregate.
244
245 6. Conveying Non-Source Forms.
246
247 You may convey a covered work in object code form under the terms
248 of sections 4 and 5, provided that you also convey the
249 machine-readable Corresponding Source under the terms of this License,
250 in one of these ways:
251
252 a) Convey the object code in, or embodied in, a physical product
253 (including a physical distribution medium), accompanied by the
254 Corresponding Source fixed on a durable physical medium
255 customarily used for software interchange.
256
257 b) Convey the object code in, or embodied in, a physical product
258 (including a physical distribution medium), accompanied by a
259 written offer, valid for at least three years and valid for as
260 long as you offer spare parts or customer support for that product
261 model, to give anyone who possesses the object code either (1) a
262 copy of the Corresponding Source for all the software in the
263 product that is covered by this License, on a durable physical
264 medium customarily used for software interchange, for a price no
265 more than your reasonable cost of physically performing this
266 conveying of source, or (2) access to copy the
267 Corresponding Source from a network server at no charge.
268
269 c) Convey individual copies of the object code with a copy of the
270 written offer to provide the Corresponding Source. This
271 alternative is allowed only occasionally and noncommercially, and
272 only if you received the object code with such an offer, in accord
273 with subsection 6b.
274
275 d) Convey the object code by offering access from a designated
276 place (gratis or for a charge), and offer equivalent access to the
277 Corresponding Source in the same way through the same place at no
278 further charge. You need not require recipients to copy the
279 Corresponding Source along with the object code. If the place to
280 copy the object code is a network server, the Corresponding Source
281 may be on a different server (operated by you or a third party)
282 that supports equivalent copying facilities, provided you maintain
283 clear directions next to the object code saying where to find the
284 Corresponding Source. Regardless of what server hosts the
285 Corresponding Source, you remain obligated to ensure that it is
286 available for as long as needed to satisfy these requirements.
287
288 e) Convey the object code using peer-to-peer transmission, provided
289 you inform other peers where the object code and Corresponding
290 Source of the work are being offered to the general public at no
291 charge under subsection 6d.
292
293 A separable portion of the object code, whose source code is excluded
294 from the Corresponding Source as a System Library, need not be
295 included in conveying the object code work.
296
297 A "User Product" is either (1) a "consumer product", which means any
298 tangible personal property which is normally used for personal, family,
299 or household purposes, or (2) anything designed or sold for incorporation
300 into a dwelling. In determining whether a product is a consumer product,
301 doubtful cases shall be resolved in favor of coverage. For a particular
302 product received by a particular user, "normally used" refers to a
303 typical or common use of that class of product, regardless of the status
304 of the particular user or of the way in which the particular user
305 actually uses, or expects or is expected to use, the product. A product
306 is a consumer product regardless of whether the product has substantial
307 commercial, industrial or non-consumer uses, unless such uses represent
308 the only significant mode of use of the product.
309
310 "Installation Information" for a User Product means any methods,
311 procedures, authorization keys, or other information required to install
312 and execute modified versions of a covered work in that User Product from
313 a modified version of its Corresponding Source. The information must
314 suffice to ensure that the continued functioning of the modified object
315 code is in no case prevented or interfered with solely because
316 modification has been made.
317
318 If you convey an object code work under this section in, or with, or
319 specifically for use in, a User Product, and the conveying occurs as
320 part of a transaction in which the right of possession and use of the
321 User Product is transferred to the recipient in perpetuity or for a
322 fixed term (regardless of how the transaction is characterized), the
323 Corresponding Source conveyed under this section must be accompanied
324 by the Installation Information. But this requirement does not apply
325 if neither you nor any third party retains the ability to install
326 modified object code on the User Product (for example, the work has
327 been installed in ROM).
328
329 The requirement to provide Installation Information does not include a
330 requirement to continue to provide support service, warranty, or updates
331 for a work that has been modified or installed by the recipient, or for
332 the User Product in which it has been modified or installed. Access to a
333 network may be denied when the modification itself materially and
334 adversely affects the operation of the network or violates the rules and
335 protocols for communication across the network.
336
337 Corresponding Source conveyed, and Installation Information provided,
338 in accord with this section must be in a format that is publicly
339 documented (and with an implementation available to the public in
340 source code form), and must require no special password or key for
341 unpacking, reading or copying.
342
343 7. Additional Terms.
344
345 "Additional permissions" are terms that supplement the terms of this
346 License by making exceptions from one or more of its conditions.
347 Additional permissions that are applicable to the entire Program shall
348 be treated as though they were included in this License, to the extent
349 that they are valid under applicable law. If additional permissions
350 apply only to part of the Program, that part may be used separately
351 under those permissions, but the entire Program remains governed by
352 this License without regard to the additional permissions.
353
354 When you convey a copy of a covered work, you may at your option
355 remove any additional permissions from that copy, or from any part of
356 it. (Additional permissions may be written to require their own
357 removal in certain cases when you modify the work.) You may place
358 additional permissions on material, added by you to a covered work,
359 for which you have or can give appropriate copyright permission.
360
361 Notwithstanding any other provision of this License, for material you
362 add to a covered work, you may (if authorized by the copyright holders of
363 that material) supplement the terms of this License with terms:
364
365 a) Disclaiming warranty or limiting liability differently from the
366 terms of sections 15 and 16 of this License; or
367
368 b) Requiring preservation of specified reasonable legal notices or
369 author attributions in that material or in the Appropriate Legal
370 Notices displayed by works containing it; or
371
372 c) Prohibiting misrepresentation of the origin of that material, or
373 requiring that modified versions of such material be marked in
374 reasonable ways as different from the original version; or
375
376 d) Limiting the use for publicity purposes of names of licensors or
377 authors of the material; or
378
379 e) Declining to grant rights under trademark law for use of some
380 trade names, trademarks, or service marks; or
381
382 f) Requiring indemnification of licensors and authors of that
383 material by anyone who conveys the material (or modified versions of
384 it) with contractual assumptions of liability to the recipient, for
385 any liability that these contractual assumptions directly impose on
386 those licensors and authors.
387
388 All other non-permissive additional terms are considered "further
389 restrictions" within the meaning of section 10. If the Program as you
390 received it, or any part of it, contains a notice stating that it is
391 governed by this License along with a term that is a further
392 restriction, you may remove that term. If a license document contains
393 a further restriction but permits relicensing or conveying under this
394 License, you may add to a covered work material governed by the terms
395 of that license document, provided that the further restriction does
396 not survive such relicensing or conveying.
397
398 If you add terms to a covered work in accord with this section, you
399 must place, in the relevant source files, a statement of the
400 additional terms that apply to those files, or a notice indicating
401 where to find the applicable terms.
402
403 Additional terms, permissive or non-permissive, may be stated in the
404 form of a separately written license, or stated as exceptions;
405 the above requirements apply either way.
406
407 8. Termination.
408
409 You may not propagate or modify a covered work except as expressly
410 provided under this License. Any attempt otherwise to propagate or
411 modify it is void, and will automatically terminate your rights under
412 this License (including any patent licenses granted under the third
413 paragraph of section 11).
414
415 However, if you cease all violation of this License, then your
416 license from a particular copyright holder is reinstated (a)
417 provisionally, unless and until the copyright holder explicitly and
418 finally terminates your license, and (b) permanently, if the copyright
419 holder fails to notify you of the violation by some reasonable means
420 prior to 60 days after the cessation.
421
422 Moreover, your license from a particular copyright holder is
423 reinstated permanently if the copyright holder notifies you of the
424 violation by some reasonable means, this is the first time you have
425 received notice of violation of this License (for any work) from that
426 copyright holder, and you cure the violation prior to 30 days after
427 your receipt of the notice.
428
429 Termination of your rights under this section does not terminate the
430 licenses of parties who have received copies or rights from you under
431 this License. If your rights have been terminated and not permanently
432 reinstated, you do not qualify to receive new licenses for the same
433 material under section 10.
434
435 9. Acceptance Not Required for Having Copies.
436
437 You are not required to accept this License in order to receive or
438 run a copy of the Program. Ancillary propagation of a covered work
439 occurring solely as a consequence of using peer-to-peer transmission
440 to receive a copy likewise does not require acceptance. However,
441 nothing other than this License grants you permission to propagate or
442 modify any covered work. These actions infringe copyright if you do
443 not accept this License. Therefore, by modifying or propagating a
444 covered work, you indicate your acceptance of this License to do so.
445
446 10. Automatic Licensing of Downstream Recipients.
447
448 Each time you convey a covered work, the recipient automatically
449 receives a license from the original licensors, to run, modify and
450 propagate that work, subject to this License. You are not responsible
451 for enforcing compliance by third parties with this License.
452
453 An "entity transaction" is a transaction transferring control of an
454 organization, or substantially all assets of one, or subdividing an
455 organization, or merging organizations. If propagation of a covered
456 work results from an entity transaction, each party to that
457 transaction who receives a copy of the work also receives whatever
458 licenses to the work the party's predecessor in interest had or could
459 give under the previous paragraph, plus a right to possession of the
460 Corresponding Source of the work from the predecessor in interest, if
461 the predecessor has it or can get it with reasonable efforts.
462
463 You may not impose any further restrictions on the exercise of the
464 rights granted or affirmed under this License. For example, you may
465 not impose a license fee, royalty, or other charge for exercise of
466 rights granted under this License, and you may not initiate litigation
467 (including a cross-claim or counterclaim in a lawsuit) alleging that
468 any patent claim is infringed by making, using, selling, offering for
469 sale, or importing the Program or any portion of it.
470
471 11. Patents.
472
473 A "contributor" is a copyright holder who authorizes use under this
474 License of the Program or a work on which the Program is based. The
475 work thus licensed is called the contributor's "contributor version".
476
477 A contributor's "essential patent claims" are all patent claims
478 owned or controlled by the contributor, whether already acquired or
479 hereafter acquired, that would be infringed by some manner, permitted
480 by this License, of making, using, or selling its contributor version,
481 but do not include claims that would be infringed only as a
482 consequence of further modification of the contributor version. For
483 purposes of this definition, "control" includes the right to grant
484 patent sublicenses in a manner consistent with the requirements of
485 this License.
486
487 Each contributor grants you a non-exclusive, worldwide, royalty-free
488 patent license under the contributor's essential patent claims, to
489 make, use, sell, offer for sale, import and otherwise run, modify and
490 propagate the contents of its contributor version.
491
492 In the following three paragraphs, a "patent license" is any express
493 agreement or commitment, however denominated, not to enforce a patent
494 (such as an express permission to practice a patent or covenant not to
495 sue for patent infringement). To "grant" such a patent license to a
496 party means to make such an agreement or commitment not to enforce a
497 patent against the party.
498
499 If you convey a covered work, knowingly relying on a patent license,
500 and the Corresponding Source of the work is not available for anyone
501 to copy, free of charge and under the terms of this License, through a
502 publicly available network server or other readily accessible means,
503 then you must either (1) cause the Corresponding Source to be so
504 available, or (2) arrange to deprive yourself of the benefit of the
505 patent license for this particular work, or (3) arrange, in a manner
506 consistent with the requirements of this License, to extend the patent
507 license to downstream recipients. "Knowingly relying" means you have
508 actual knowledge that, but for the patent license, your conveying the
509 covered work in a country, or your recipient's use of the covered work
510 in a country, would infringe one or more identifiable patents in that
511 country that you have reason to believe are valid.
512
513 If, pursuant to or in connection with a single transaction or
514 arrangement, you convey, or propagate by procuring conveyance of, a
515 covered work, and grant a patent license to some of the parties
516 receiving the covered work authorizing them to use, propagate, modify
517 or convey a specific copy of the covered work, then the patent license
518 you grant is automatically extended to all recipients of the covered
519 work and works based on it.
520
521 A patent license is "discriminatory" if it does not include within
522 the scope of its coverage, prohibits the exercise of, or is
523 conditioned on the non-exercise of one or more of the rights that are
524 specifically granted under this License. You may not convey a covered
525 work if you are a party to an arrangement with a third party that is
526 in the business of distributing software, under which you make payment
527 to the third party based on the extent of your activity of conveying
528 the work, and under which the third party grants, to any of the
529 parties who would receive the covered work from you, a discriminatory
530 patent license (a) in connection with copies of the covered work
531 conveyed by you (or copies made from those copies), or (b) primarily
532 for and in connection with specific products or compilations that
533 contain the covered work, unless you entered into that arrangement,
534 or that patent license was granted, prior to 28 March 2007.
535
536 Nothing in this License shall be construed as excluding or limiting
537 any implied license or other defenses to infringement that may
538 otherwise be available to you under applicable patent law.
539
540 12. No Surrender of Others' Freedom.
541
542 If conditions are imposed on you (whether by court order, agreement or
543 otherwise) that contradict the conditions of this License, they do not
544 excuse you from the conditions of this License. If you cannot convey a
545 covered work so as to satisfy simultaneously your obligations under this
546 License and any other pertinent obligations, then as a consequence you may
547 not convey it at all. For example, if you agree to terms that obligate you
548 to collect a royalty for further conveying from those to whom you convey
549 the Program, the only way you could satisfy both those terms and this
550 License would be to refrain entirely from conveying the Program.
551
552 13. Use with the GNU Affero General Public License.
553
554 Notwithstanding any other provision of this License, you have
555 permission to link or combine any covered work with a work licensed
556 under version 3 of the GNU Affero General Public License into a single
557 combined work, and to convey the resulting work. The terms of this
558 License will continue to apply to the part which is the covered work,
559 but the special requirements of the GNU Affero General Public License,
560 section 13, concerning interaction through a network will apply to the
561 combination as such.
562
563 14. Revised Versions of this License.
564
565 The Free Software Foundation may publish revised and/or new versions of
566 the GNU General Public License from time to time. Such new versions will
567 be similar in spirit to the present version, but may differ in detail to
568 address new problems or concerns.
569
570 Each version is given a distinguishing version number. If the
571 Program specifies that a certain numbered version of the GNU General
572 Public License "or any later version" applies to it, you have the
573 option of following the terms and conditions either of that numbered
574 version or of any later version published by the Free Software
575 Foundation. If the Program does not specify a version number of the
576 GNU General Public License, you may choose any version ever published
577 by the Free Software Foundation.
578
579 If the Program specifies that a proxy can decide which future
580 versions of the GNU General Public License can be used, that proxy's
581 public statement of acceptance of a version permanently authorizes you
582 to choose that version for the Program.
583
584 Later license versions may give you additional or different
585 permissions. However, no additional obligations are imposed on any
586 author or copyright holder as a result of your choosing to follow a
587 later version.
588
589 15. Disclaimer of Warranty.
590
591 THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599
600 16. Limitation of Liability.
601
602 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 SUCH DAMAGES.
611
612 17. Interpretation of Sections 15 and 16.
613
614 If the disclaimer of warranty and limitation of liability provided
615 above cannot be given local legal effect according to their terms,
616 reviewing courts shall apply local law that most closely approximates
617 an absolute waiver of all civil liability in connection with the
618 Program, unless a warranty or assumption of liability accompanies a
619 copy of the Program in return for a fee.
620
621 END OF TERMS AND CONDITIONS
622
623 How to Apply These Terms to Your New Programs
624
625 If you develop a new program, and you want it to be of the greatest
626 possible use to the public, the best way to achieve this is to make it
627 free software which everyone can redistribute and change under these terms.
628
629 To do so, attach the following notices to the program. It is safest
630 to attach them to the start of each source file to most effectively
631 state the exclusion of warranty; and each file should have at least
632 the "copyright" line and a pointer to where the full notice is found.
633
634 <one line to give the program's name and a brief idea of what it does.>
635 Copyright (C) <year> <name of author>
636
637 This program is free software: you can redistribute it and/or modify
638 it under the terms of the GNU General Public License as published by
639 the Free Software Foundation, either version 3 of the License, or
640 (at your option) any later version.
641
642 This program is distributed in the hope that it will be useful,
643 but WITHOUT ANY WARRANTY; without even the implied warranty of
644 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 GNU General Public License for more details.
646
647 You should have received a copy of the GNU General Public License
648 along with this program. If not, see <http://www.gnu.org/licenses/>.
649
650 Also add information on how to contact you by electronic and paper mail.
651
652 If the program does terminal interaction, make it output a short
653 notice like this when it starts in an interactive mode:
654
655 <program> Copyright (C) <year> <name of author>
656 This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 This is free software, and you are welcome to redistribute it
658 under certain conditions; type `show c' for details.
659
660 The hypothetical commands `show w' and `show c' should show the appropriate
661 parts of the General Public License. Of course, your program's commands
662 might be different; for a GUI interface, you would use an "about box".
663
664 You should also get your employer (if you work as a programmer) or school,
665 if any, to sign a "copyright disclaimer" for the program, if necessary.
666 For more information on this, and how to apply and follow the GNU GPL, see
667 <http://www.gnu.org/licenses/>.
668
669 The GNU General Public License does not permit incorporating your program
670 into proprietary programs. If your program is a subroutine library, you
671 may consider it more useful to permit linking proprietary applications with
672 the library. If this is what you want to do, use the GNU Lesser General
673 Public License instead of this License. But first, please read
674 <http://www.gnu.org/philosophy/why-not-lgpl.html>.
@@ -0,0 +1,211
1 #### Version released on [09.06.12] ####
2
3 Quick Summary:
4 - Items (arrows, text,...)
5 - Layers (easier control over rendering order)
6 - New antialiasing system (Each objects controls own antialiasing with setAntialiased)
7 - Performance Improvements
8 - improved pixel-precise drawing
9 - easier shared library creation/usage
10
11 Changes that (might) break backward compatibility:
12 - enum QCPGraph::ScatterSymbol was moved to QCP namespace (now QCP::ScatterSymbol).
13 This replace should fix your code: "QCPGraph::ss" -> "QCP::ss"
14 - enum QCustomPlot::AntialiasedElement and flag QCustomPlot::AntialiasedElements was moved to QCP namespace
15 This replace should fix your code: "QCustomPlot::ae" -> "QCP::ae"
16 - the meaning of QCustomPlot::setAntialiasedElements has changed slightly: It is now an override to force elements to be antialiased. If you want to force
17 elements to not be drawn antialiased, use the new setNotAntialiasedElements. If an element is mentioned in neither of those functions, it now controls
18 its antialiasing itself via its "setAntialiased" function(s). (e.g. QCPAxis::setAntialiased(bool), QCPAbstractPlottable::setAntialiased(bool),
19 QCPAbstractPlottable::setAntialiasedScatters(bool), etc.)
20 - QCPAxis::setTickVector and QCPAxis::setTickVectorLabels no longer take a pointer but a const reference of the respective QVector as parameter.
21 (handing over a pointer didn't give any noticeable performance benefits but was inconsistent with the rest of the interface)
22 - Equally QCPAxis::tickVector and QCPAxis::tickVectorLabels don't return by pointer but by value now
23 - QCustomPlot::savePngScaled was removed, its purpose is now included as optional parameter "scale" of savePng.
24 - If you have derived from QCPAbstractPlottable: all selectTest functions now consistently take the argument "const QPointF &pos" which is the test point in pixel coordinates.
25 (the argument there was "double key, double value" in plot coordinates, before).
26 - QCPAbstractPlottable, QCPAxis and QCPLegend now inherit from QCPLayerable
27 - If you have derived from QCPAbstractPlottable: the draw method signature has changed from "draw (..) const" to "draw (..)", i.e. the method
28 is not const anymore. This allows the draw function of your plottable to perform buffering operations, if necessary.
29
30 Added features:
31 - Item system: QCPAbstractItem, QCPItemAnchor, QCPItemPosition, QCPLineEnding. Allows placing of lines, arrows, text, pixmaps etc.
32 - New Items: QCPItemStraightLine, QCPItemLine, QCPItemCurve, QCPItemEllipse, QCPItemRect, QCPItemPixmap, QCPItemText, QCPItemBracket, QCPItemTracer
33 - QCustomPlot::addItem/itemCount/item/removeItem/selectedItems
34 - signals QCustomPlot::itemClicked/itemDoubleClicked
35 - the QCustomPlot interactions property now includes iSelectItems (for selection of QCPAbstractItem)
36 - QCPLineEnding. Represents the different styles a line/curve can end (e.g. different arrows, circle, square, bar, etc.), see e.g. QCPItemCurve::setHead
37 - Layer system: QCPLayerable, QCPLayer. Allows more sophisticated control over drawing order and a kind of grouping.
38 - QCPAbstractPlottable, QCPAbstractItem, QCPAxis, QCPGrid, QCPLegend are layerables and derive from QCPLayerable
39 - QCustomPlot::addLayer/moveLayer/removeLayer/setCurrentLayer/layer/currentLayer/layerCount
40 - Initially there are three layers: "grid", "main", and "axes". The "main" layer is initially empty and set as current layer, so new plottables/items are put there.
41 - QCustomPlot::viewport now makes the previously inaccessible viewport rect read-only-accessible (needed that for item-interface)
42 - PNG export now allows transparent background by calling QCustomPlot::setColor(Qt::transparent) before savePng
43 - QCPStatisticalBox outlier symbols may now be all scatter symbols, not only hardcoded circles.
44 - perfect precision of scatter symbol/error bar drawing and clipping in both antialiased and non-antialiased mode, by introducing QCPPainter
45 that works around some QPainter bugs/inconveniences. Further, more complex symbols like ssCrossSquare used to look crooked, now they look good.
46 - new antialiasing control system: Each drawing element now has its own "setAntialiased" function to control whether it is drawn antialiased.
47 - QCustomPlot::setAntialiasedElements and QCustomPlot::setNotAntialiasedElements can be used to override the individual settings.
48 - Subclasses of QCPAbstractPlottable can now use the convenience functions like applyFillAntialiasingHint or applyScattersAntialiasingHint to
49 easily make their drawing code comply with the overall antialiasing system.
50 - QCustomPlot::setNoAntialiasingOnDrag allows greatly improved performance and responsiveness by temporarily disabling all antialiasing while
51 the user is dragging axis ranges
52 - QCPGraph can now show scatter symbols at data points and hide its line (see QCPGraph::setScatterStyle, setScatterSize, setScatterPixmap, setLineStyle)
53 - Grid drawing code was sourced out from QCPAxis to QCPGrid. QCPGrid is mainly an internal class and every QCPAxis owns one. The grid interface still
54 works through QCPAxis and hasn't changed. The separation allows the grid to be drawn on a different layer as the axes, such that e.g. a graph can
55 be above the grid but below the axes.
56 - QCustomPlot::hasPlottable(plottable), returns whether the QCustomPlot contains the plottable
57 - QCustomPlot::setPlottingHint/setPlottingHints, plotting hints control details about the plotting quality/speed
58 - export to jpg and bmp added (QCustomPlot::saveJpg/saveBmp), as well as control over compression quality for png and jpg
59 - multi-select-modifier may now be specified with QCustomPlot::setMultiSelectModifier and is not fixed to Ctrl anymore
60
61 Bugfixes:
62 - fixed QCustomPlot ignores replot after it had size (0,0) even if size becomes valid again
63 - on Windows, a repaint used to be delayed during dragging/zooming of a complex plot, until the drag operation was done.
64 This was fixed, i.e. repaints are forced after a replot() call. See QCP::phForceRepaint and setPlottingHints.
65 - when using the raster paintengine and exporting to scaled PNG, pen widths are now scaled correctly (QPainter bug workaround via QCPPainter)
66 - PDF export now respects QCustomPlot background color (QCustomPlot::setColor), also Qt::transparent
67 - fixed a bug on QCPBars and QCPStatisticalBox where auto-rescaling of axis would fail when all data is very small (< 1e-11)
68 - fixed mouse event propagation bug that prevented range dragging from working on KDE (GNU/Linux)
69 - fixed a compiler warning on 64-bit systems due to pointer cast to int instead of quintptr in a qDebug output
70
71 Other:
72 - Added support for easier shared library creation (including examples for compiling and using QCustomPlot as shared library)
73 - QCustomPlot now has the Qt::WA_OpaquePaintEvent widget attribute (gives slightly improved performance).
74 - QCP::aeGraphs (enum QCP::AntialiasedElement, previously QCustomPlot::aeGraphs) has been marked deprecated since version 02.02.12 and
75 was now removed. Use QCP::aePlottables instead.
76 - optional performance-quality-tradeoff for solid graph lines (see QCustomPlot::setPlottingHints).
77 - marked data classes and QCPRange as Q_MOVABLE_TYPE
78 - replaced usage of own macro FUNCNAME with Qt macro Q_FUNC_INFO
79 - QCustomPlot now returns a minimum size hint of 50*50
80
81 #### Version released on [31.03.12] ####
82
83 Changes that (might) break backward compatibility:
84 - QCPAbstractLegendItem now inherits from QObject
85 - mousePress, mouseMove and mouseRelease signals are now emitted before and not after any QCustomPlot processing (range dragging, selecting, etc.)
86
87 Added features:
88 - Interaction system: now allows selecting of objects like plottables, axes, legend and plot title, see QCustomPlot::setInteractions documentation
89 - Interaction system for plottables:
90 - setSelectable, setSelected, setSelectedPen, setSelectedBrush, selectTest on QCPAbstractPlottable and all derived plottables
91 - setSelectionTolerance on QCustomPlot
92 - selectedPlottables and selectedGraphs on QCustomPlot (returns the list of currently selected plottables/graphs)
93 - Interaction system for axes:
94 - setSelectable, setSelected, setSelectedBasePen, setSelectedTickPen, setSelectedSubTickPen, setSelectedLabelFont, setSelectedTickLabelFont,
95 setSelectedLabelColor, setSelectedTickLabelColor, selectTest on QCPAxis
96 - selectedAxes on QCustomPlot (returns a list of the axes that currently have selected parts)
97 - Interaction system for legend:
98 - setSelectable, setSelected, setSelectedBorderPen, setSelectedIconBorderPen, setSelectedBrush, setSelectedFont, setSelectedTextColor, selectedItems on QCPLegend
99 - setSelectedFont, setSelectedTextColor, setSelectable, setSelected on QCPAbstractLegendItem
100 - selectedLegends on QCustomPlot
101 - Interaction system for title:
102 - setSelectedTitleFont, setSelectedTitleColor, setTitleSelected on QCustomPlot
103 - new signals in accordance with the interaction system:
104 - selectionChangedByUser on QCustomPlot
105 - selectionChanged on QCPAbstractPlottable
106 - selectionChanged on QCPAxis
107 - selectionChanged on QCPLegend and QCPAbstractLegendItem
108 - plottableClick, legendClick, axisClick, titleClick, plottableDoubleClick, legendDoubleClick, axisDoubleClick, titleDoubleClick on QCustomPlot
109 - QCustomPlot::deselectAll (deselects everything, i.e. axes and plottables)
110 - QCPAbstractPlottable::pixelsToCoords (inverse function to the already existing coordsToPixels function)
111 - QCPRange::contains(double value)
112 - QCPAxis::setLabelColor and setTickLabelColor
113 - QCustomPlot::setTitleColor
114 - QCustomPlot now emits beforeReplot and afterReplot signals. Note that it is safe to make two customPlots mutually call eachothers replot functions
115 in one of these slots, it will not cause an infinite loop. (usefull for synchronizing axes ranges between two customPlots, because setRange alone doesn't replot)
116 - If the Qt version is 4.7 or greater, the tick label strings in date-time-mode now support sub-second accuracy (e.g. with format like "hh:mm:ss.zzz").
117
118 Bugfixes:
119 - tick labels/margins should no longer oscillate by one pixel when dragging range or replotting repeatedly while changing e.g. data. This
120 was caused by a bug in Qt's QFontMetrics::boundingRect function when the font has an integer point size (probably some rounding problem).
121 The fix hence consists of creating a temporary font (only for bounding-box calculation) which is 0.05pt larger and thus avoiding the
122 jittering rounding outcome.
123 - tick label, axis label and plot title colors used to be undefined. This was fixed by providing explicit color properties.
124
125 Other:
126 - fixed some glitches in the documentation
127 - QCustomPlot::replot and QCustomPlot::rescaleAxes are now slots
128
129 #### Version released on [02.02.12] ####
130
131 Changes that break backward compatibility:
132 - renamed all secondary classes from QCustomPlot[...] to QCP[...]:
133 QCustomPlotAxis -> QCPAxis
134 QCustomPlotGraph -> QCPGraph
135 QCustomPlotRange -> QCPRange
136 QCustomPlotData -> QCPData
137 QCustomPlotDataMap -> QCPDataMap
138 QCustomPlotLegend -> QCPLegend
139 QCustomPlotDataMapIterator -> QCPDataMapIterator
140 QCustomPlotDataMutableMapIterator -> QCPDataMutableMapIterator
141 A simple search and replace on all code files should make your code run again, e.g. consider the regex "QCustomPlot(?=[AGRDL])" -> "QCP".
142 Make sure not to just replace "QCustomPlot" with "QCP" because the main class QCustomPlot hasn't changed to QCP.
143 This change was necessary because class names became unhandy, pardon my bad naming decision in the beginning.
144 - QCPAxis::tickLength() and QCPAxis::subTickLength() now each split into two functions for inward and outward ticks (tickLengthIn/tickLengthOut).
145 - QCPLegend now uses QCPAbstractLegendItem to carry item data (before, the legend was passed QCPGraphs directly)
146 - QCustomPlot::addGraph() now doesn't return the index of the created graph anymore, but a pointer to the created QCPGraph.
147 - QCustomPlot::setAutoAddGraphToLegend is replaced by setAutoAddPlottableToLegend
148
149 Added features:
150 - Reversed axis range with QCPAxis::setRangeReversed(bool)
151 - Tick labels are now only drawn if not clipped by the viewport (widget border) on the sides (e.g. left and right on a horizontal axis).
152 - Zerolines. Like grid lines only with a separate pen (QCPAxis::setZeroLinePen), at tick position zero.
153 - Outward ticks. QCPAxis::setTickLength/setSubTickLength now accepts two arguments for inward and outward tick length. This doesn't break
154 backward compatibility because the second argument (outward) has default value zero and thereby a call with one argument hasn't changed its meaning.
155 - QCPGraph now inherits from QCPAbstractPlottable
156 - QCustomPlot::addPlottable/plottable/removePlottable/clearPlottables added to interface with the new QCPAbstractPlottable-based system. The simpler interface
157 which only acts on QCPGraphs (addGraph, graph, removeGraph, etc.) was adapted internally and is kept for backward compatibility and ease of use.
158 - QCPLegend items for plottables (e.g. graphs) can automatically wrap their texts to fit the widths, see QCPLegend::setMinimumSize and QCPPlottableLegendItem::setTextWrap.
159 - QCustomPlot::rescaleAxes. Adapts axis ranges to show all plottables/graphs, by calling QCPAbstractPlottable::rescaleAxes on all plottables in the plot.
160 - QCPCurve. For plotting of parametric curves.
161 - QCPBars. For plotting of bar charts.
162 - QCPStatisticalBox. For statistical box plots.
163
164 Bugfixes:
165 - Fixed QCustomPlot::removeGraph(int) not being able to remove graph index 0
166 - made QCustomPlot::replot() abort painting when painter initialization fails (e.g. because width/height of QCustomPlot is zero)
167 - The distance of the axis label from the axis ignored the tick label padding, this could have caused overlapping axis labels and tick labels
168 - fixed memory leak in QCustomPlot (dtor didn't delete legend)
169 - fixed bug that prevented QCPAxis::setRangeLower/Upper from setting the value to exactly 0.
170
171 Other:
172 - Changed default error bar handle size (QCustomPlotGraph::setErrorBarSize) from 4 to 6.
173 - Removed QCustomPlotDataFetcher. Was deprecated and not used class.
174 - Extended documentation, especially class descriptions.
175
176 #### Version released on [15.01.12] ####
177
178 Changes that (might) break backward compatibility:
179 - QCustomPlotGraph now inherits from QObject
180
181 Added features:
182 - Added axis background pixmap (QCustomPlot::setAxisBackground, setAxisBackgroundScaled, setAxisBackgroundScaledMode)
183 - Added width and height parameter on PDF export function QCustomPlot::savePdf(). This now allows PDF export to
184 have arbitrary dimensions, independent of the current geometry of the QCustomPlot.
185 - Added overload of QCustomPlot::removeGraph that takes QCustomPlotGraph* as parameter, instead the index of the graph
186 - Added all enums to the Qt meta system via Q_ENUMS(). The enums can now be transformed
187 to QString values easily with the Qt meta system, which makes saving state e.g. as XML
188 significantly nicer.
189 - added typedef QMapIterator<double,QCustomPlotData> QCustomPlotDataMapIterator
190 and typedef QMutableMapIterator<double,QCustomPlotData> QCustomPlotDataMutableMapIterator
191 for improved information hiding, when using iterators outside QCustomPlot code
192
193 Bugfixes:
194 - Fixed savePngScaled. Axis/label drawing functions used to reset the painter transform
195 and thereby break savePngScaled. Now they buffer the current transform and restore it afterwards.
196 - Fixed some glitches in the doxygen comments (affects documentation only)
197
198 Other:
199 - Changed the default tickLabelPadding of top axis from 3 to 6 pixels. Looks better.
200 - Changed the default QCustomPlot::setAntialiasedElements setting: Graph fills are now antialiased
201 by default. That's a bit slower, but makes fill borders look better.
202
203 #### Version released on [19.11.11] ####
204
205 Changes that break backward compatibility:
206 - QCustomPlotAxis: tickFont and setTickFont renamed to tickLabelFont and setTickLabelFont (for
207 naming consistency)
208
209 Other:
210 - QCustomPlotAxis: Added rotated tick labels, see setTickLabelRotation
211
This diff has been collapsed as it changes many lines, (15033 lines changed) Show them Hide them
@@ -0,0 +1,15033
1 /***************************************************************************
2 ** **
3 ** QCustomPlot, a simple to use, modern plotting widget for Qt **
4 ** Copyright (C) 2012 Emanuel Eichhammer **
5 ** **
6 ** This program is free software: you can redistribute it and/or modify **
7 ** it under the terms of the GNU General Public License as published by **
8 ** the Free Software Foundation, either version 3 of the License, or **
9 ** (at your option) any later version. **
10 ** **
11 ** This program is distributed in the hope that it will be useful, **
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of **
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the **
14 ** GNU General Public License for more details. **
15 ** **
16 ** You should have received a copy of the GNU General Public License **
17 ** along with this program. If not, see http://www.gnu.org/licenses/. **
18 ** **
19 ****************************************************************************
20 ** Author: Emanuel Eichhammer **
21 ** Website/Contact: http://www.WorksLikeClockwork.com/ **
22 ** Date: 09.06.12 **
23 ****************************************************************************/
24
25 /*! \mainpage %QCustomPlot Documentation
26
27 Below is a brief overview of and guide to the classes and their relations. If you are new to
28 QCustomPlot and just want to start using it, it's recommended to look at the examples/tutorials
29 at
30
31 http://www.WorksLikeClockWork.com/index.php/components/qt-plotting-widget
32
33 This documentation is especially helpful when you're familiar with the basic concept of how to use
34 %QCustomPlot and you wish to learn more about specific functionality.
35
36 \section simpleoverview Simplified Class Overview
37
38 \image latex ClassesOverviewSimplified.png "" width=1.2\textwidth
39 \image html ClassesOverviewSimplified.png
40 <center>Simplified diagram of most important classes, view the \ref classoverview "Class Overview" to see a full overview.</center>
41
42 The central widget which displays the plottables and axes on its surface is QCustomPlot. Usually,
43 you don't create the axes yourself, but you use the ones already inside every QCustomPlot
44 instance (xAxis, yAxis, xAxis2, yAxis2).
45
46 \section plottables Plottables
47
48 \a Plottables are classes that display any kind of data inside the QCustomPlot. They all derive
49 from QCPAbstractPlottable. For example, the QCPGraph class is a plottable that displays a graph
50 inside the plot with different line styles, scatter styles, filling etc.
51
52 Since plotting graphs is such a dominant use case, QCustomPlot has a special interface for working
53 with QCPGraph plottables, that makes it very easy to handle them:\n
54 You create a new graph with QCustomPlot::addGraph and access them with QCustomPlot::graph.
55
56 For all other plottables, you need to use the normal plottable interface:\n
57 First, you create an instance of the plottable you want, e.g.
58 \code
59 QCPCurve *newCurve = new QCPCurve(customPlot->xAxis, customPlot->yAxis);\endcode
60 add it to the customPlot with QCustomPlot::addPlottable:
61 \code
62 customPlot->addPlottable(newCurve);\endcode
63 and then modify the properties of the newly created plottable via <tt>newCurve</tt>.
64
65 Plottables (including graphs) can be retrieved via QCustomPlot::plottable. Since the return type
66 of that function is the abstract base class of all plottables, QCPAbstractPlottable, you will
67 probably want to qobject_cast (or dynamic_cast) the returned pointer to the respective plottable
68 subclass. (As usual, if the cast returns zero, the plottable wasn't of that specific subclass.)
69
70 All further interfacing with plottables (e.g how to set data) is specific to the plottable type.
71 See the documentations of the subclasses: QCPGraph, QCPCurve, QCPBars, QCPStatisticalBox.
72
73 \section axes Controlling the Axes
74
75 As mentioned, QCustomPlot has four axes by default: \a xAxis (bottom), \a yAxis (left), \a xAxis2
76 (top), \a yAxis2 (right).
77
78 Their range is handled by the simple QCPRange class. You can set the range with the
79 QCPAxis::setRange function. By default, the axes represent a linear scale. To set a logarithmic
80 scale, set QCPAxis::setScaleType to QCPAxis::stLogarithmic. The logarithm base can be set freely
81 with QCPAxis::setScaleLogBase.
82
83 By default, an axis automatically creates and labels ticks in a sensible manner, i.e. with a tick
84 interval that's pleasing to the viewer. See the following functions for tick manipulation:\n
85 QCPAxis::setTicks, QCPAxis::setAutoTicks, QCPAxis::setAutoTickCount, QCPAxis::setAutoTickStep,
86 QCPAxis::setTickLabels, QCPAxis::setTickLabelType, QCPAxis::setTickLabelRotation,
87 QCPAxis::setTickStep, QCPAxis::setTickLength,...
88
89 Each axis can be given an axis label (e.g. "Voltage [mV]") with QCPAxis::setLabel.
90
91 The distance of an axis backbone to the respective QCustomPlot widget border is called its margin.
92 Normally, the margins are calculated automatically. To change this, set QCustomPlot::setAutoMargin
93 to false and set the margins manually with QCustomPlot::setMargin.
94
95 \section legend Plot Legend
96
97 Every QCustomPlot owns a QCPLegend (as \a legend). That's a small window inside the plot which
98 lists the plottables with an icon of the plottable line/symbol and a description. The Description
99 is retrieved from the plottable name (QCPAbstractPlottable::setName). Plottables can be added and
100 removed from the legend via \ref QCPAbstractPlottable::addToLegend and \ref
101 QCPAbstractPlottable::removeFromLegend. By default, adding a plottable to QCustomPlot
102 automatically adds it to the legend, too. This behaviour can be modified with the
103 QCustomPlot::setAutoAddPlottableToLegend property.
104
105 The QCPLegend provides an interface to access, add and remove legend items directly, too. See
106 QCPLegend::item, QCPLegend::itemWithPlottable, QCPLegend::addItem, QCPLegend::removeItem for
107 example.
108
109 \section userinteraction User Interactions
110
111 QCustomPlot currently supports dragging axis ranges with the mouse (\ref
112 QCustomPlot::setRangeDrag), zooming axis ranges with the mouse wheel (\ref
113 QCustomPlot::setRangeZoom) and a complete selection mechanism of most objects.
114
115 The availability of these interactions is controlled with \ref QCustomPlot::setInteractions. For
116 details about the interaction system, see the documentation there.
117
118 Further, QCustomPlot always emits corresponding signals, when objects are clicked or
119 doubleClicked. See \ref QCustomPlot::plottableClick, \ref QCustomPlot::plottableDoubleClick
120 and \ref QCustomPlot::axisClick for example.
121
122 \section items Items
123
124 Apart from plottables there is another category of plot objects that are important: Items. The
125 base class of all items is QCPAbstractItem. An item sets itself apart from plottables in that
126 it's not necessarily bound to any axes. This means it may also be positioned in absolute pixel
127 coordinates or placed at a relative position on the axis rect. Further it usually doesn't
128 represent data directly but acts as decoration, emphasis, description etc.
129
130 Multiple items can be arranged in a parent-child-hierarchy allowing for dynamical behaviour. For
131 example, you could place the head of an arrow at a certain plot coordinate, so it always points
132 to some important part of your data. The tail of the arrow can be fixed at a text label item
133 which always resides in the top center of the axis rect (independent of where the user drags the
134 axis ranges).
135
136 For a more detailed introduction, see the QCPAbstractItem documentation, and from there the
137 documentations of the individual built-in items, to find out how to use them.
138
139 \section performancetweaks Performance Tweaks
140
141 Although QCustomPlot is quite fast, some features like semi-transparent fills and antialiasing
142 can cause a significant slow down. Here are some thoughts on how to increase performance. By far
143 the most time is spent in the drawing functions, specifically the drawing of graphs. For maximum
144 performance, consider the following (most recommended/effective measures first):
145
146 \li use Qt 4.8.0 and up. Performance has doubled or tripled with respect to Qt 4.7.4. However they broke QPainter,
147 drawing pixel precise things, e.g. scatters, isn't possible with Qt 4.8.0/1. So it's a performance vs. plot
148 quality tradeoff when switching to Qt 4.8.
149 \li To increase responsiveness during dragging, consider setting \ref QCustomPlot::setNoAntialiasingOnDrag to true.
150 \li On X11 (linux), avoid the (slow) native drawing system, use raster by supplying
151 "-graphicssystem raster" as command line argument or calling QApplication::setGraphicsSystem("raster")
152 before creating the QApplication object.
153 \li On all operating systems, use OpenGL hardware acceleration by supplying "-graphicssystem
154 opengl" as command line argument or calling QApplication::setGraphicsSystem("opengl"). If OpenGL
155 is available, this will slightly decrease the quality of antialiasing, but extremely increase
156 performance especially with alpha (semi-transparent) fills, much antialiasing and a large
157 QCustomPlot drawing surface. Note however, that the maximum frame rate might be constrained by
158 the vertical sync frequency of your monitor (VSync can be disabled in the graphics card driver
159 configuration). So for simple plots (where the potential framerate is far above 60 frames per
160 second), OpenGL acceleration might achieve numerically lower frame rates than the other
161 graphics systems, because they are not capped at the VSync frequency.
162 \li Avoid any kind of alpha (transparency), especially in fills
163 \li Avoid any kind of antialiasing, especially in graph lines (see QCustomPlot::setNotAntialiasedElements)
164 \li Avoid repeatedly setting the complete data set with QCPGraph::setData. Use QCPGraph::addData instead, if most
165 data points stay unchanged, e.g. in a running measurement.
166 \li Set the \a copy parameter of the setData functions to false, so only pointers get
167 transferred. (Relevant only if preparing data maps with a large number of points, i.e. over 10000)
168 */
169
170 /*! \page classoverview Class Overview
171
172 \image latex ClassesOverview.png "Overview of all classes and their relations" width=1.2\textwidth
173 \image html ClassesOverview.png "Overview of all classes and their relations"
174
175 */
176
177 #include "qcustomplot.h"
178
179 // ================================================================================
180 // =================== QCPData
181 // ================================================================================
182
183 /*! \class QCPData
184 \brief Holds the data of one single data point for QCPGraph.
185
186 The stored data is:
187 \li \a key: coordinate on the key axis of this data point
188 \li \a value: coordinate on the value axis of this data point
189 \li \a keyErrorMinus: negative error in the key dimension (for error bars)
190 \li \a keyErrorPlus: positive error in the key dimension (for error bars)
191 \li \a valueErrorMinus: negative error in the value dimension (for error bars)
192 \li \a valueErrorPlus: positive error in the value dimension (for error bars)
193
194 \see QCPDataMap
195 */
196
197 /*!
198 Constructs a data point with key, value and all errors set to zero.
199 */
200 QCPData::QCPData() :
201 key(0),
202 value(0),
203 keyErrorPlus(0),
204 keyErrorMinus(0),
205 valueErrorPlus(0),
206 valueErrorMinus(0)
207 {
208 }
209
210 /*!
211 Constructs a data point with the specified \a key and \a value. All errors are set to zero.
212 */
213 QCPData::QCPData(double key, double value) :
214 key(key),
215 value(value),
216 keyErrorPlus(0),
217 keyErrorMinus(0),
218 valueErrorPlus(0),
219 valueErrorMinus(0)
220 {
221 }
222
223 // ================================================================================
224 // =================== QCPCurveData
225 // ================================================================================
226
227 /*! \class QCPCurveData
228 \brief Holds the data of one single data point for QCPCurve.
229
230 The stored data is:
231 \li \a t: the free parameter of the curve at this curve point (cp. the mathematical vector <em>(x(t), y(t))</em>)
232 \li \a key: coordinate on the key axis of this curve point
233 \li \a value: coordinate on the value axis of this curve point
234
235 \see QCPCurveDataMap
236 */
237
238 /*!
239 Constructs a curve data point with t, key and value set to zero.
240 */
241 QCPCurveData::QCPCurveData() :
242 t(0),
243 key(0),
244 value(0)
245 {
246 }
247
248 /*!
249 Constructs a curve data point with the specified \a t, \a key and \a value.
250 */
251 QCPCurveData::QCPCurveData(double t, double key, double value) :
252 t(t),
253 key(key),
254 value(value)
255 {
256 }
257
258
259 // ================================================================================
260 // =================== QCPBarData
261 // ================================================================================
262
263 /*! \class QCPBarData
264 \brief Holds the data of one single data point (one bar) for QCPBars.
265
266 The stored data is:
267 \li \a key: coordinate on the key axis of this bar
268 \li \a value: height coordinate on the value axis of this bar
269
270 \see QCPBarDataaMap
271 */
272
273 /*!
274 Constructs a bar data point with key and value set to zero.
275 */
276 QCPBarData::QCPBarData() :
277 key(0),
278 value(0)
279 {
280 }
281
282 /*!
283 Constructs a bar data point with the specified \a key and \a value.
284 */
285 QCPBarData::QCPBarData(double key, double value) :
286 key(key),
287 value(value)
288 {
289 }
290
291 // ================================================================================
292 // =================== QCPGraph
293 // ================================================================================
294
295 /*! \class QCPGraph
296 \brief A plottable representing a graph in a plot.
297
298 Usually QCustomPlot creates it internally via QCustomPlot::addGraph and the resulting instance is
299 accessed via QCustomPlot::graph.
300
301 To plot data, assign it with the \ref setData or \ref addData functions.
302
303 \section appearance Changing the appearance
304
305 The appearance of the graph is mainly determined by the line style, scatter style, brush and pen
306 of the graph (\ref setLineStyle, \ref setScatterStyle, \ref setBrush, \ref setPen).
307
308 \subsection filling Filling under or between graphs
309
310 QCPGraph knows two types of fills: Normal graph fills towards the zero-value-line parallel to
311 the key axis of the graph, and fills between two graphs, called channel fills. To enable a fill,
312 just set a brush with \ref setBrush which is neither Qt::NoBrush nor fully transparent.
313
314 By default, a normal fill towards the zero-value-line will be drawn. To set up a channel fill
315 between this graph and another one, call \ref setChannelFillGraph with the other graph as
316 parameter.
317
318 \see QCustomPlot::addGraph, QCustomPlot::graph, QCPLegend::addGraph
319 */
320
321 /*!
322 Constructs a graph which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
323 axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
324 the same orientation. If either of these restrictions is violated, a corresponding message is
325 printed to the debug output (qDebug), the construction is not aborted, though.
326
327 The constructed QCPGraph can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
328 then takes ownership of the graph.
329
330 To directly create a graph inside a plot, you can also use the simpler QCustomPlot::addGraph function.
331 */
332 QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) :
333 QCPAbstractPlottable(keyAxis, valueAxis)
334 {
335 mData = new QCPDataMap;
336
337 setPen(QPen(Qt::blue));
338 setErrorPen(QPen(Qt::black));
339 setBrush(Qt::NoBrush);
340 setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
341 setSelectedBrush(Qt::NoBrush);
342
343 setLineStyle(lsLine);
344 setScatterStyle(QCP::ssNone);
345 setScatterSize(6);
346 setErrorType(etNone);
347 setErrorBarSize(6);
348 setErrorBarSkipSymbol(true);
349 setChannelFillGraph(0);
350 }
351
352 QCPGraph::~QCPGraph()
353 {
354 if (mParentPlot)
355 {
356 // if another graph has a channel fill towards this graph, set it to zero
357 for (int i=0; i<mParentPlot->graphCount(); ++i)
358 {
359 if (mParentPlot->graph(i)->channelFillGraph() == this)
360 mParentPlot->graph(i)->setChannelFillGraph(0);
361 }
362 }
363 delete mData;
364 }
365
366 /*!
367 Replaces the current data with the provided \a data.
368
369 If \a copy is set to true, data points in \a data will only be copied. if false, the graph
370 takes ownership of the passed data and replaces the internal data pointer with it. This is
371 significantly faster than copying for large datasets.
372 */
373 void QCPGraph::setData(QCPDataMap *data, bool copy)
374 {
375 if (copy)
376 {
377 *mData = *data;
378 } else
379 {
380 delete mData;
381 mData = data;
382 }
383 }
384
385 /*! \overload
386
387 Replaces the current data with the provided points in \a key and \a value pairs. The provided
388 vectors should have equal length. Else, the number of added points will be the size of the
389 smallest vector.
390 */
391 void QCPGraph::setData(const QVector<double> &key, const QVector<double> &value)
392 {
393 mData->clear();
394 int n = key.size();
395 n = qMin(n, value.size());
396 QCPData newData;
397 for (int i=0; i<n; ++i)
398 {
399 newData.key = key[i];
400 newData.value = value[i];
401 mData->insertMulti(newData.key, newData);
402 }
403 }
404
405 /*!
406 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
407 symmetrical value error of the data points are set to the values in \a valueError.
408 For error bars to show appropriately, see \ref setErrorType.
409 The provided vectors should have equal length. Else, the number of added points will be the size of the
410 smallest vector.
411
412 For asymmetrical errors (plus different from minus), see the overloaded version of this function.
413 */
414 void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueError)
415 {
416 mData->clear();
417 int n = key.size();
418 n = qMin(n, value.size());
419 n = qMin(n, valueError.size());
420 QCPData newData;
421 for (int i=0; i<n; ++i)
422 {
423 newData.key = key[i];
424 newData.value = value[i];
425 newData.valueErrorMinus = valueError[i];
426 newData.valueErrorPlus = valueError[i];
427 mData->insertMulti(key[i], newData);
428 }
429 }
430
431 /*!
432 \overload
433 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
434 negative value error of the data points are set to the values in \a valueErrorMinus, the positive
435 value error to \a valueErrorPlus.
436 For error bars to show appropriately, see \ref setErrorType.
437 The provided vectors should have equal length. Else, the number of added points will be the size of the
438 smallest vector.
439 */
440 void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus)
441 {
442 mData->clear();
443 int n = key.size();
444 n = qMin(n, value.size());
445 n = qMin(n, valueErrorMinus.size());
446 n = qMin(n, valueErrorPlus.size());
447 QCPData newData;
448 for (int i=0; i<n; ++i)
449 {
450 newData.key = key[i];
451 newData.value = value[i];
452 newData.valueErrorMinus = valueErrorMinus[i];
453 newData.valueErrorPlus = valueErrorPlus[i];
454 mData->insertMulti(key[i], newData);
455 }
456 }
457
458 /*!
459 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
460 symmetrical key error of the data points are set to the values in \a keyError.
461 For error bars to show appropriately, see \ref setErrorType.
462 The provided vectors should have equal length. Else, the number of added points will be the size of the
463 smallest vector.
464
465 For asymmetrical errors (plus different from minus), see the overloaded version of this function.
466 */
467 void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError)
468 {
469 mData->clear();
470 int n = key.size();
471 n = qMin(n, value.size());
472 n = qMin(n, keyError.size());
473 QCPData newData;
474 for (int i=0; i<n; ++i)
475 {
476 newData.key = key[i];
477 newData.value = value[i];
478 newData.keyErrorMinus = keyError[i];
479 newData.keyErrorPlus = keyError[i];
480 mData->insertMulti(key[i], newData);
481 }
482 }
483
484 /*!
485 \overload
486 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
487 negative key error of the data points are set to the values in \a keyErrorMinus, the positive
488 key error to \a keyErrorPlus.
489 For error bars to show appropriately, see \ref setErrorType.
490 The provided vectors should have equal length. Else, the number of added points will be the size of the
491 smallest vector.
492 */
493 void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus)
494 {
495 mData->clear();
496 int n = key.size();
497 n = qMin(n, value.size());
498 n = qMin(n, keyErrorMinus.size());
499 n = qMin(n, keyErrorPlus.size());
500 QCPData newData;
501 for (int i=0; i<n; ++i)
502 {
503 newData.key = key[i];
504 newData.value = value[i];
505 newData.keyErrorMinus = keyErrorMinus[i];
506 newData.keyErrorPlus = keyErrorPlus[i];
507 mData->insertMulti(key[i], newData);
508 }
509 }
510
511 /*!
512 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
513 symmetrical key and value errors of the data points are set to the values in \a keyError and \a valueError.
514 For error bars to show appropriately, see \ref setErrorType.
515 The provided vectors should have equal length. Else, the number of added points will be the size of the
516 smallest vector.
517
518 For asymmetrical errors (plus different from minus), see the overloaded version of this function.
519 */
520 void QCPGraph::setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError, const QVector<double> &valueError)
521 {
522 mData->clear();
523 int n = key.size();
524 n = qMin(n, value.size());
525 n = qMin(n, valueError.size());
526 n = qMin(n, keyError.size());
527 QCPData newData;
528 for (int i=0; i<n; ++i)
529 {
530 newData.key = key[i];
531 newData.value = value[i];
532 newData.keyErrorMinus = keyError[i];
533 newData.keyErrorPlus = keyError[i];
534 newData.valueErrorMinus = valueError[i];
535 newData.valueErrorPlus = valueError[i];
536 mData->insertMulti(key[i], newData);
537 }
538 }
539
540 /*!
541 \overload
542 Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
543 negative key and value errors of the data points are set to the values in \a keyErrorMinus and \a valueErrorMinus. The positive
544 key and value errors are set to the values in \a keyErrorPlus \a valueErrorPlus.
545 For error bars to show appropriately, see \ref setErrorType.
546 The provided vectors should have equal length. Else, the number of added points will be the size of the
547 smallest vector.
548 */
549 void QCPGraph::setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus)
550 {
551 mData->clear();
552 int n = key.size();
553 n = qMin(n, value.size());
554 n = qMin(n, valueErrorMinus.size());
555 n = qMin(n, valueErrorPlus.size());
556 n = qMin(n, keyErrorMinus.size());
557 n = qMin(n, keyErrorPlus.size());
558 QCPData newData;
559 for (int i=0; i<n; ++i)
560 {
561 newData.key = key[i];
562 newData.value = value[i];
563 newData.keyErrorMinus = keyErrorMinus[i];
564 newData.keyErrorPlus = keyErrorPlus[i];
565 newData.valueErrorMinus = valueErrorMinus[i];
566 newData.valueErrorPlus = valueErrorPlus[i];
567 mData->insertMulti(key[i], newData);
568 }
569 }
570
571
572 /*!
573 Sets how the single data points are connected in the plot or how they are represented visually
574 apart from the scatter symbol. For scatter-only plots, set \a ls to \ref lsNone and \ref
575 setScatterStyle to the desired scatter style.
576
577 \see setScatterStyle
578 */
579 void QCPGraph::setLineStyle(LineStyle ls)
580 {
581 mLineStyle = ls;
582 }
583
584 /*!
585 Sets the visual appearance of single data points in the plot. If set to \ref QCP::ssNone, no scatter points
586 are drawn (e.g. for line-only-plots with appropriate line style).
587
588 \see ScatterStyle, setLineStyle
589 */
590 void QCPGraph::setScatterStyle(QCP::ScatterStyle ss)
591 {
592 mScatterStyle = ss;
593 }
594
595 /*!
596 This defines how big (in pixels) single scatters are drawn, if scatter style (\ref
597 setScatterStyle) isn't \ref QCP::ssNone, \ref QCP::ssDot or \ref QCP::ssPixmap. Floating point values are
598 allowed for fine grained control over optical appearance with antialiased painting.
599
600 \see ScatterStyle
601 */
602 void QCPGraph::setScatterSize(double size)
603 {
604 mScatterSize = size;
605 }
606
607 /*!
608 If the scatter style (\ref setScatterStyle) is set to ssPixmap, this function defines the QPixmap
609 that will be drawn centered on the data point coordinate.
610
611 \see ScatterStyle
612 */
613 void QCPGraph::setScatterPixmap(const QPixmap &pixmap)
614 {
615 mScatterPixmap = pixmap;
616 }
617
618 /*!
619 Sets which kind of error bars (Key Error, Value Error or both) should be drawn on each data
620 point. If you set \a errorType to something other than \ref etNone, make sure to actually pass
621 error data via the specific setData functions along with the data points (e.g. \ref
622 setDataValueError, \ref setDataKeyError, \ref setDataBothError).
623
624 \see ErrorType
625 */
626 void QCPGraph::setErrorType(ErrorType errorType)
627 {
628 mErrorType = errorType;
629 }
630
631 /*!
632 Sets the pen with which the error bars will be drawn.
633 \see setErrorBarSize, setErrorType
634 */
635 void QCPGraph::setErrorPen(const QPen &pen)
636 {
637 mErrorPen = pen;
638 }
639
640 /*!
641 Sets the width of the handles at both ends of an error bar in pixels.
642 */
643 void QCPGraph::setErrorBarSize(double size)
644 {
645 mErrorBarSize = size;
646 }
647
648 /*!
649 If \a enabled is set to true, the error bar will not be drawn as a solid line under the scatter symbol but
650 leave some free space around the symbol.
651
652 This feature uses the current scatter size (\ref setScatterSize) to determine the size of the
653 area to leave blank. So when drawing Pixmaps as scatter points (\ref QCP::ssPixmap), the scatter size
654 must be set manually to a value corresponding to the size of the Pixmap, if the error bars should
655 leave gaps to its boundaries.
656 */
657 void QCPGraph::setErrorBarSkipSymbol(bool enabled)
658 {
659 mErrorBarSkipSymbol = enabled;
660 }
661
662 /*!
663 Sets the target graph for filling the area between this graph and \a targetGraph with the current
664 brush (\ref setBrush).
665
666 When \a targetGraph is set to 0, a normal graph fill will be produced. This means, when the brush
667 is not Qt::NoBrush or fully transparent, a fill all the way to the zero-value-line parallel to
668 the key axis of this graph will be drawn. To disable any filling, set the brush to Qt::NoBrush.
669 \see setBrush
670 */
671 void QCPGraph::setChannelFillGraph(QCPGraph *targetGraph)
672 {
673 // prevent setting channel target to this graph itself:
674 if (targetGraph == this)
675 {
676 qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself";
677 mChannelFillGraph = 0;
678 return;
679 }
680 // prevent setting channel target to a graph not in the plot:
681 if (targetGraph && targetGraph->mParentPlot != mParentPlot)
682 {
683 qDebug() << Q_FUNC_INFO << "targetGraph not in same plot";
684 mChannelFillGraph = 0;
685 return;
686 }
687
688 mChannelFillGraph = targetGraph;
689 }
690
691 /*!
692 Adds the provided data points in \a dataMap to the current data.
693 \see removeData
694 */
695 void QCPGraph::addData(const QCPDataMap &dataMap)
696 {
697 mData->unite(dataMap);
698 }
699
700 /*! \overload
701 Adds the provided single data point in \a data to the current data.
702 \see removeData
703 */
704 void QCPGraph::addData(const QCPData &data)
705 {
706 mData->insertMulti(data.key, data);
707 }
708
709 /*! \overload
710 Adds the provided single data point as \a key and \a value pair to the current data.
711 \see removeData
712 */
713 void QCPGraph::addData(double key, double value)
714 {
715 QCPData newData;
716 newData.key = key;
717 newData.value = value;
718 mData->insertMulti(newData.key, newData);
719 }
720
721 /*! \overload
722 Adds the provided data points as \a key and \a value pairs to the current data.
723 \see removeData
724 */
725 void QCPGraph::addData(const QVector<double> &keys, const QVector<double> &values)
726 {
727 int n = qMin(keys.size(), values.size());
728 QCPData newData;
729 for (int i=0; i<n; ++i)
730 {
731 newData.key = keys[i];
732 newData.value = values[i];
733 mData->insertMulti(newData.key, newData);
734 }
735 }
736
737 /*!
738 Removes all data points with keys smaller than \a key.
739 \see addData, clearData
740 */
741 void QCPGraph::removeDataBefore(double key)
742 {
743 QCPDataMap::iterator it = mData->begin();
744 while (it != mData->end() && it.key() < key)
745 it = mData->erase(it);
746 }
747
748 /*!
749 Removes all data points with keys greater than \a key.
750 \see addData, clearData
751 */
752 void QCPGraph::removeDataAfter(double key)
753 {
754 if (mData->isEmpty()) return;
755 QCPDataMap::iterator it = mData->upperBound(key);
756 while (it != mData->end())
757 it = mData->erase(it);
758 }
759
760 /*!
761 Removes all data points with keys between \a fromKey and \a toKey.
762 if \a fromKey is greater or equal to \a toKey, the function does nothing. To remove
763 a single data point with known key, use \ref removeData(double key).
764
765 \see addData, clearData
766 */
767 void QCPGraph::removeData(double fromKey, double toKey)
768 {
769 if (fromKey >= toKey || mData->isEmpty()) return;
770 QCPDataMap::iterator it = mData->upperBound(fromKey);
771 QCPDataMap::iterator itEnd = mData->upperBound(toKey);
772 while (it != itEnd)
773 it = mData->erase(it);
774 }
775
776 /*! \overload
777
778 Removes a single data point at \a key. If the position is not known with absolute precision,
779 consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval around
780 the suspected position, depeding on the precision with which the key is known.
781
782 \see addData, clearData
783 */
784 void QCPGraph::removeData(double key)
785 {
786 mData->remove(key);
787 }
788
789 /*!
790 Removes all data points.
791 \see removeData, removeDataAfter, removeDataBefore
792 */
793 void QCPGraph::clearData()
794 {
795 mData->clear();
796 }
797
798 /* inherits documentation from base class */
799 double QCPGraph::selectTest(const QPointF &pos) const
800 {
801 if (mData->isEmpty() || !mVisible)
802 return -1;
803
804 return pointDistance(pos);
805 }
806
807 /*! \overload
808
809 Allows to define whether error bars are taken into consideration when determining the new axis
810 range.
811 */
812 void QCPGraph::rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const
813 {
814 rescaleKeyAxis(onlyEnlarge, includeErrorBars);
815 rescaleValueAxis(onlyEnlarge, includeErrorBars);
816 }
817
818 /*! \overload
819
820 Allows to define whether error bars (of kind \ref QCPGraph::etKey) are taken into consideration
821 when determining the new axis range.
822 */
823 void QCPGraph::rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const
824 {
825 // this code is a copy of QCPAbstractPlottable::rescaleKeyAxis with the only change
826 // that getKeyRange is passed the includeErrorBars value.
827 if (mData->isEmpty()) return;
828
829 SignDomain signDomain = sdBoth;
830 if (mKeyAxis->scaleType() == QCPAxis::stLogarithmic)
831 signDomain = (mKeyAxis->range().upper < 0 ? sdNegative : sdPositive);
832
833 bool validRange;
834 QCPRange newRange = getKeyRange(validRange, signDomain, includeErrorBars);
835
836 if (validRange)
837 {
838 if (onlyEnlarge)
839 {
840 if (mKeyAxis->range().lower < newRange.lower)
841 newRange.lower = mKeyAxis->range().lower;
842 if (mKeyAxis->range().upper > newRange.upper)
843 newRange.upper = mKeyAxis->range().upper;
844 }
845 mKeyAxis->setRange(newRange);
846 }
847 }
848
849 /*! \overload
850
851 Allows to define whether error bars (of kind \ref QCPGraph::etValue) are taken into consideration
852 when determining the new axis range.
853 */
854 void QCPGraph::rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const
855 {
856 // this code is a copy of QCPAbstractPlottable::rescaleValueAxis with the only change
857 // is that getValueRange is passed the includeErrorBars value.
858 if (mData->isEmpty()) return;
859
860 SignDomain signDomain = sdBoth;
861 if (mValueAxis->scaleType() == QCPAxis::stLogarithmic)
862 signDomain = (mValueAxis->range().upper < 0 ? sdNegative : sdPositive);
863
864 bool validRange;
865 QCPRange newRange = getValueRange(validRange, signDomain, includeErrorBars);
866
867 if (validRange)
868 {
869 if (onlyEnlarge)
870 {
871 if (mValueAxis->range().lower < newRange.lower)
872 newRange.lower = mValueAxis->range().lower;
873 if (mValueAxis->range().upper > newRange.upper)
874 newRange.upper = mValueAxis->range().upper;
875 }
876 mValueAxis->setRange(newRange);
877 }
878 }
879
880 /* inherits documentation from base class */
881 void QCPGraph::draw(QCPPainter *painter)
882 {
883 if (mKeyAxis->range().size() <= 0 || mData->isEmpty()) return;
884 if (mLineStyle == lsNone && mScatterStyle == QCP::ssNone) return;
885
886 // allocate line and (if necessary) point vectors:
887 QVector<QPointF> *lineData = new QVector<QPointF>;
888 QVector<QCPData> *pointData = 0;
889 if (mScatterStyle != QCP::ssNone)
890 pointData = new QVector<QCPData>;
891
892 // fill vectors with data appropriate to plot style:
893 getPlotData(lineData, pointData);
894
895 // draw fill of graph:
896 drawFill(painter, lineData);
897
898 // draw line:
899 if (mLineStyle == lsImpulse)
900 drawImpulsePlot(painter, lineData);
901 else if (mLineStyle != lsNone)
902 drawLinePlot(painter, lineData); // also step plots can be drawn as a line plot
903
904 // draw scatters:
905 if (pointData)
906 drawScatterPlot(painter, pointData);
907
908 // free allocated line and point vectors:
909 delete lineData;
910 if (pointData)
911 delete pointData;
912 }
913
914 /* inherits documentation from base class */
915 void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRect &rect) const
916 {
917 // draw fill:
918 if (mBrush.style() != Qt::NoBrush)
919 {
920 applyFillAntialiasingHint(painter);
921 painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
922 }
923 // draw line vertically centered:
924 if (mLineStyle != lsNone)
925 {
926 applyDefaultAntialiasingHint(painter);
927 painter->setPen(mPen);
928 painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens
929 }
930 // draw scatter symbol:
931 if (mScatterStyle != QCP::ssNone)
932 {
933 if (mScatterStyle == QCP::ssPixmap && (mScatterPixmap.size().width() > rect.width() || mScatterPixmap.size().height() > rect.height()))
934 {
935 // handle pixmap scatters that are larger than legend icon rect separately.
936 // We resize them and draw them manually, instead of calling drawScatter:
937 QSize newSize = mScatterPixmap.size();
938 newSize.scale(rect.size(), Qt::KeepAspectRatio);
939 QRect targetRect;
940 targetRect.setSize(newSize);
941 targetRect.moveCenter(rect.center());
942 bool smoothBackup = painter->testRenderHint(QPainter::SmoothPixmapTransform);
943 painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
944 painter->drawPixmap(targetRect, mScatterPixmap);
945 painter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);
946 } else
947 {
948 applyScattersAntialiasingHint(painter);
949 painter->setPen(mPen);
950 painter->drawScatter(QRectF(rect).center().x(), QRectF(rect).center().y(), mScatterSize, mScatterStyle);
951 }
952 }
953 }
954
955 /*!
956 \internal
957 This function branches out to the line style specific "get(...)PlotData" functions, according to the
958 line style of the graph.
959 \param lineData will be filled with raw points that will be drawn with the according draw functions, e.g. \ref drawLinePlot and \ref drawImpulsePlot.
960 These aren't necessarily the original data points, since for step plots for example, additional points are needed for drawing lines that make up steps.
961 If the line style of the graph is \ref lsNone, the \a lineData vector will be left untouched.
962 \param pointData will be filled with the original data points so \ref drawScatterPlot can draw the scatter symbols accordingly. If no scatters need to be
963 drawn, i.e. scatter style is \ref QCP::ssNone, pass 0 as \a pointData, and this step will be skipped.
964
965 \see getScatterPlotData, getLinePlotData, getStepLeftPlotData, getStepRightPlotData, getStepCenterPlotData, getImpulsePlotData
966 */
967 void QCPGraph::getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
968 {
969 switch(mLineStyle)
970 {
971 case lsNone: getScatterPlotData(pointData); break;
972 case lsLine: getLinePlotData(lineData, pointData); break;
973 case lsStepLeft: getStepLeftPlotData(lineData, pointData); break;
974 case lsStepRight: getStepRightPlotData(lineData, pointData); break;
975 case lsStepCenter: getStepCenterPlotData(lineData, pointData); break;
976 case lsImpulse: getImpulsePlotData(lineData, pointData); break;
977 }
978 }
979
980 /*!
981 \internal
982 If line style is \ref lsNone and scatter style is not \ref QCP::ssNone, this function serves at providing the
983 visible data points in \a pointData, so the \ref drawScatterPlot function can draw the scatter points
984 accordingly.
985
986 If line style is not \ref lsNone, this function is not called and the data for the scatter points
987 are (if needed) calculated inside the corresponding other "get(...)PlotData" functions.
988 \see drawScatterPlot
989 */
990 void QCPGraph::getScatterPlotData(QVector<QCPData> *pointData) const
991 {
992 if (!pointData) return;
993
994 // get visible data range:
995 QCPDataMap::const_iterator lower, upper;
996 int dataCount;
997 getVisibleDataBounds(lower, upper, dataCount);
998 // prepare vectors:
999 if (pointData)
1000 pointData->resize(dataCount);
1001
1002 // position data points:
1003 QCPDataMap::const_iterator it = lower;
1004 QCPDataMap::const_iterator upperEnd = upper+1;
1005 int i = 0;
1006 if (mKeyAxis->orientation() == Qt::Vertical)
1007 {
1008 while (it != upperEnd)
1009 {
1010 (*pointData)[i] = it.value();
1011 ++i;
1012 ++it;
1013 }
1014 } else // key axis is horizontal
1015 {
1016 while (it != upperEnd)
1017 {
1018 (*pointData)[i] = it.value();
1019 ++i;
1020 ++it;
1021 }
1022 }
1023 }
1024
1025 /*!
1026 \internal
1027 Places the raw data points needed for a normal linearly connected plot in \a lineData.
1028
1029 As for all plot data retrieval functions, \a pointData just contains all unaltered data (scatter)
1030 points that are visible, for drawing scatter points, if necessary. If drawing scatter points is
1031 disabled (i.e. scatter style \ref QCP::ssNone), pass 0 as \a pointData, and the function will skip
1032 filling the vector.
1033 \see drawLinePlot
1034 */
1035 void QCPGraph::getLinePlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
1036 {
1037 // get visible data range:
1038 QCPDataMap::const_iterator lower, upper;
1039 int dataCount;
1040 getVisibleDataBounds(lower, upper, dataCount);
1041 // prepare vectors:
1042 if (lineData)
1043 {
1044 // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
1045 lineData->reserve(dataCount+2);
1046 lineData->resize(dataCount);
1047 }
1048 if (pointData)
1049 pointData->resize(dataCount);
1050
1051 // position data points:
1052 QCPDataMap::const_iterator it = lower;
1053 QCPDataMap::const_iterator upperEnd = upper+1;
1054 int i = 0;
1055 if (mKeyAxis->orientation() == Qt::Vertical)
1056 {
1057 while (it != upperEnd)
1058 {
1059 if (pointData)
1060 (*pointData)[i] = it.value();
1061 (*lineData)[i].setX(mValueAxis->coordToPixel(it.value().value));
1062 (*lineData)[i].setY(mKeyAxis->coordToPixel(it.key()));
1063 ++i;
1064 ++it;
1065 }
1066 } else // key axis is horizontal
1067 {
1068 while (it != upperEnd)
1069 {
1070 if (pointData)
1071 (*pointData)[i] = it.value();
1072 (*lineData)[i].setX(mKeyAxis->coordToPixel(it.key()));
1073 (*lineData)[i].setY(mValueAxis->coordToPixel(it.value().value));
1074 ++i;
1075 ++it;
1076 }
1077 }
1078 }
1079
1080 /*!
1081 \internal
1082 Places the raw data points needed for a step plot with left oriented steps in \a lineData.
1083
1084 As for all plot data retrieval functions, \a pointData just contains all unaltered data (scatter)
1085 points that are visible, for drawing scatter points, if necessary. If drawing scatter points is
1086 disabled (i.e. scatter style \ref QCP::ssNone), pass 0 as \a pointData, and the function will skip
1087 filling the vector.
1088 \see drawLinePlot
1089 */
1090 void QCPGraph::getStepLeftPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
1091 {
1092 // get visible data range:
1093 QCPDataMap::const_iterator lower, upper;
1094 int dataCount;
1095 getVisibleDataBounds(lower, upper, dataCount);
1096 // prepare vectors:
1097 if (lineData)
1098 {
1099 // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
1100 // multiplied by 2 because step plot needs two polyline points per one actual data point
1101 lineData->reserve(dataCount*2+2);
1102 lineData->resize(dataCount*2);
1103 }
1104 if (pointData)
1105 pointData->resize(dataCount);
1106
1107 // position data points:
1108 QCPDataMap::const_iterator it = lower;
1109 QCPDataMap::const_iterator upperEnd = upper+1;
1110 int i = 0;
1111 int ipoint = 0;
1112 if (mKeyAxis->orientation() == Qt::Vertical)
1113 {
1114 double lastValue = mValueAxis->coordToPixel(it.value().value);
1115 double key;
1116 while (it != upperEnd)
1117 {
1118 if (pointData)
1119 {
1120 (*pointData)[ipoint] = it.value();
1121 ++ipoint;
1122 }
1123 key = mKeyAxis->coordToPixel(it.key());
1124 (*lineData)[i].setX(lastValue);
1125 (*lineData)[i].setY(key);
1126 ++i;
1127 lastValue = mValueAxis->coordToPixel(it.value().value);
1128 (*lineData)[i].setX(lastValue);
1129 (*lineData)[i].setY(key);
1130 ++i;
1131 ++it;
1132 }
1133 } else // key axis is horizontal
1134 {
1135 double lastValue = mValueAxis->coordToPixel(it.value().value);
1136 double key;
1137 while (it != upperEnd)
1138 {
1139 if (pointData)
1140 {
1141 (*pointData)[ipoint] = it.value();
1142 ++ipoint;
1143 }
1144 key = mKeyAxis->coordToPixel(it.key());
1145 (*lineData)[i].setX(key);
1146 (*lineData)[i].setY(lastValue);
1147 ++i;
1148 lastValue = mValueAxis->coordToPixel(it.value().value);
1149 (*lineData)[i].setX(key);
1150 (*lineData)[i].setY(lastValue);
1151 ++i;
1152 ++it;
1153 }
1154 }
1155 }
1156
1157 /*!
1158 \internal
1159 Places the raw data points needed for a step plot with right oriented steps in \a lineData.
1160
1161 As for all plot data retrieval functions, \a pointData just contains all unaltered data (scatter)
1162 points that are visible, for drawing scatter points, if necessary. If drawing scatter points is
1163 disabled (i.e. scatter style \ref QCP::ssNone), pass 0 as \a pointData, and the function will skip
1164 filling the vector.
1165 \see drawLinePlot
1166 */
1167 void QCPGraph::getStepRightPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
1168 {
1169 // get visible data range:
1170 QCPDataMap::const_iterator lower, upper;
1171 int dataCount;
1172 getVisibleDataBounds(lower, upper, dataCount);
1173 // prepare vectors:
1174 if (lineData)
1175 {
1176 // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
1177 // multiplied by 2 because step plot needs two polyline points per one actual data point
1178 lineData->reserve(dataCount*2+2);
1179 lineData->resize(dataCount*2);
1180 }
1181 if (pointData)
1182 pointData->resize(dataCount);
1183
1184 // position points:
1185 QCPDataMap::const_iterator it = lower;
1186 QCPDataMap::const_iterator upperEnd = upper+1;
1187 int i = 0;
1188 int ipoint = 0;
1189 if (mKeyAxis->orientation() == Qt::Vertical)
1190 {
1191 double lastKey = mKeyAxis->coordToPixel(it.key());
1192 double value;
1193 while (it != upperEnd)
1194 {
1195 if (pointData)
1196 {
1197 (*pointData)[ipoint] = it.value();
1198 ++ipoint;
1199 }
1200 value = mValueAxis->coordToPixel(it.value().value);
1201 (*lineData)[i].setX(value);
1202 (*lineData)[i].setY(lastKey);
1203 ++i;
1204 lastKey = mKeyAxis->coordToPixel(it.key());
1205 (*lineData)[i].setX(value);
1206 (*lineData)[i].setY(lastKey);
1207 ++i;
1208 ++it;
1209 }
1210 } else // key axis is horizontal
1211 {
1212 double lastKey = mKeyAxis->coordToPixel(it.key());
1213 double value;
1214 while (it != upperEnd)
1215 {
1216 if (pointData)
1217 {
1218 (*pointData)[ipoint] = it.value();
1219 ++ipoint;
1220 }
1221 value = mValueAxis->coordToPixel(it.value().value);
1222 (*lineData)[i].setX(lastKey);
1223 (*lineData)[i].setY(value);
1224 ++i;
1225 lastKey = mKeyAxis->coordToPixel(it.key());
1226 (*lineData)[i].setX(lastKey);
1227 (*lineData)[i].setY(value);
1228 ++i;
1229 ++it;
1230 }
1231 }
1232 }
1233
1234 /*!
1235 \internal
1236 Places the raw data points needed for a step plot with centered steps in \a lineData.
1237
1238 As for all plot data retrieval functions, \a pointData just contains all unaltered data (scatter)
1239 points that are visible, for drawing scatter points, if necessary. If drawing scatter points is
1240 disabled (i.e. scatter style \ref QCP::ssNone), pass 0 as \a pointData, and the function will skip
1241 filling the vector.
1242 \see drawLinePlot
1243 */
1244 void QCPGraph::getStepCenterPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
1245 {
1246 // get visible data range:
1247 QCPDataMap::const_iterator lower, upper;
1248 int dataCount;
1249 getVisibleDataBounds(lower, upper, dataCount);
1250 // prepare vectors:
1251 if (lineData)
1252 {
1253 // added 2 to reserve memory for lower/upper fill base points that might be needed for base fill
1254 // multiplied by 2 because step plot needs two polyline points per one actual data point
1255 lineData->reserve(dataCount*2+2);
1256 lineData->resize(dataCount*2);
1257 }
1258 if (pointData)
1259 pointData->resize(dataCount);
1260
1261 // position points:
1262 QCPDataMap::const_iterator it = lower;
1263 QCPDataMap::const_iterator upperEnd = upper+1;
1264 int i = 0;
1265 int ipoint = 0;
1266 if (mKeyAxis->orientation() == Qt::Vertical)
1267 {
1268 double lastKey = mKeyAxis->coordToPixel(it.key());
1269 double lastValue = mValueAxis->coordToPixel(it.value().value);
1270 double key;
1271 if (pointData)
1272 {
1273 (*pointData)[ipoint] = it.value();
1274 ++ipoint;
1275 }
1276 (*lineData)[i].setX(lastValue);
1277 (*lineData)[i].setY(lastKey);
1278 ++it;
1279 ++i;
1280 while (it != upperEnd)
1281 {
1282 if (pointData)
1283 {
1284 (*pointData)[ipoint] = it.value();
1285 ++ipoint;
1286 }
1287 key = (mKeyAxis->coordToPixel(it.key())-lastKey)*0.5 + lastKey;
1288 (*lineData)[i].setX(lastValue);
1289 (*lineData)[i].setY(key);
1290 ++i;
1291 lastValue = mValueAxis->coordToPixel(it.value().value);
1292 lastKey = mKeyAxis->coordToPixel(it.key());
1293 (*lineData)[i].setX(lastValue);
1294 (*lineData)[i].setY(key);
1295 ++it;
1296 ++i;
1297 }
1298 (*lineData)[i].setX(lastValue);
1299 (*lineData)[i].setY(lastKey);
1300 } else // key axis is horizontal
1301 {
1302 double lastKey = mKeyAxis->coordToPixel(it.key());
1303 double lastValue = mValueAxis->coordToPixel(it.value().value);
1304 double key;
1305 if (pointData)
1306 {
1307 (*pointData)[ipoint] = it.value();
1308 ++ipoint;
1309 }
1310 (*lineData)[i].setX(lastKey);
1311 (*lineData)[i].setY(lastValue);
1312 ++it;
1313 ++i;
1314 while (it != upperEnd)
1315 {
1316 if (pointData)
1317 {
1318 (*pointData)[ipoint] = it.value();
1319 ++ipoint;
1320 }
1321 key = (mKeyAxis->coordToPixel(it.key())-lastKey)*0.5 + lastKey;
1322 (*lineData)[i].setX(key);
1323 (*lineData)[i].setY(lastValue);
1324 ++i;
1325 lastValue = mValueAxis->coordToPixel(it.value().value);
1326 lastKey = mKeyAxis->coordToPixel(it.key());
1327 (*lineData)[i].setX(key);
1328 (*lineData)[i].setY(lastValue);
1329 ++it;
1330 ++i;
1331 }
1332 (*lineData)[i].setX(lastKey);
1333 (*lineData)[i].setY(lastValue);
1334 }
1335 }
1336
1337 /*!
1338 \internal
1339 Places the raw data points needed for an impulse plot in \a lineData.
1340
1341 As for all plot data retrieval functions, \a pointData just contains all unaltered data (scatter)
1342 points that are visible, for drawing scatter points, if necessary. If drawing scatter points is
1343 disabled (i.e. scatter style \ref QCP::ssNone), pass 0 as \a pointData, and the function will skip
1344 filling the vector.
1345 \see drawImpulsePlot
1346 */
1347 void QCPGraph::getImpulsePlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const
1348 {
1349 // get visible data range:
1350 QCPDataMap::const_iterator lower, upper;
1351 int dataCount;
1352 getVisibleDataBounds(lower, upper, dataCount);
1353 // prepare vectors:
1354 if (lineData)
1355 {
1356 // no need to reserve 2 extra points, because there is no fill for impulse plot
1357 lineData->resize(dataCount*2);
1358 }
1359 if (pointData)
1360 pointData->resize(dataCount);
1361
1362 // position data points:
1363 QCPDataMap::const_iterator it = lower;
1364 QCPDataMap::const_iterator upperEnd = upper+1;
1365 int i = 0;
1366 int ipoint = 0;
1367 if (mKeyAxis->orientation() == Qt::Vertical)
1368 {
1369 double zeroPointX = mValueAxis->coordToPixel(0);
1370 double key;
1371 while (it != upperEnd)
1372 {
1373 if (pointData)
1374 {
1375 (*pointData)[ipoint] = it.value();
1376 ++ipoint;
1377 }
1378 key = mKeyAxis->coordToPixel(it.key());
1379 (*lineData)[i].setX(zeroPointX);
1380 (*lineData)[i].setY(key);
1381 ++i;
1382 (*lineData)[i].setX(mValueAxis->coordToPixel(it.value().value));
1383 (*lineData)[i].setY(key);
1384 ++i;
1385 ++it;
1386 }
1387 } else // key axis is horizontal
1388 {
1389 double zeroPointY = mValueAxis->coordToPixel(0);
1390 double key;
1391 while (it != upperEnd)
1392 {
1393 if (pointData)
1394 {
1395 (*pointData)[ipoint] = it.value();
1396 ++ipoint;
1397 }
1398 key = mKeyAxis->coordToPixel(it.key());
1399 (*lineData)[i].setX(key);
1400 (*lineData)[i].setY(zeroPointY);
1401 ++i;
1402 (*lineData)[i].setX(key);
1403 (*lineData)[i].setY(mValueAxis->coordToPixel(it.value().value));
1404 ++i;
1405 ++it;
1406 }
1407 }
1408 }
1409
1410 /*!
1411 \internal
1412 Draws the fill of the graph with the specified brush. If the fill is a normal "base" fill, i.e.
1413 under the graph toward the zero-value-line, only the \a lineData is required (and two extra points
1414 at the zero-value-line, which are added by \ref addFillBasePoints and removed by \ref removeFillBasePoints
1415 after the fill drawing is done).
1416
1417 If the fill is a channel fill between this graph and another graph (mChannelFillGraph), the more complex
1418 polygon is calculated with the \ref getChannelFillPolygon function.
1419 \see drawLinePlot
1420 */
1421 void QCPGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lineData) const
1422 {
1423 if (mLineStyle == lsImpulse) return; // fill doesn't make sense for impulse plot
1424 if (mainBrush().style() == Qt::NoBrush || mainBrush().color().alpha() == 0) return;
1425
1426 applyFillAntialiasingHint(painter);
1427 if (!mChannelFillGraph)
1428 {
1429 // draw base fill under graph, fill goes all the way to the zero-value-line:
1430 addFillBasePoints(lineData);
1431 painter->setPen(Qt::NoPen);
1432 painter->setBrush(mainBrush());
1433 painter->drawPolygon(QPolygonF(*lineData));
1434 removeFillBasePoints(lineData);
1435 } else
1436 {
1437 // draw channel fill between this graph and mChannelFillGraph:
1438 painter->setPen(Qt::NoPen);
1439 painter->setBrush(mainBrush());
1440 painter->drawPolygon(getChannelFillPolygon(lineData));
1441 }
1442 }
1443
1444 /*! \internal
1445
1446 Draws scatter symbols at every data point passed in \a pointData. scatter symbols are independent of
1447 the line style and are always drawn if scatter style is not \ref QCP::ssNone. Hence, the \a pointData vector
1448 is outputted by all "get(...)PlotData" functions, together with the (line style dependent) line data.
1449 \see drawLinePlot, drawImpulsePlot
1450 */
1451 void QCPGraph::drawScatterPlot(QCPPainter *painter, QVector<QCPData> *pointData) const
1452 {
1453 // draw error bars:
1454 if (mErrorType != etNone)
1455 {
1456 applyErrorBarsAntialiasingHint(painter);
1457 painter->setPen(mErrorPen);
1458 if (mKeyAxis->orientation() == Qt::Vertical)
1459 {
1460 for (int i=0; i<pointData->size(); ++i)
1461 drawError(painter, mValueAxis->coordToPixel(pointData->at(i).value), mKeyAxis->coordToPixel(pointData->at(i).key), pointData->at(i));
1462 } else
1463 {
1464 for (int i=0; i<pointData->size(); ++i)
1465 drawError(painter, mKeyAxis->coordToPixel(pointData->at(i).key), mValueAxis->coordToPixel(pointData->at(i).value), pointData->at(i));
1466 }
1467 }
1468
1469 // draw scatter point symbols:
1470 applyScattersAntialiasingHint(painter);
1471 painter->setPen(mainPen());
1472 painter->setBrush(mainBrush());
1473 painter->setScatterPixmap(mScatterPixmap);
1474 if (mKeyAxis->orientation() == Qt::Vertical)
1475 {
1476 for (int i=0; i<pointData->size(); ++i)
1477 painter->drawScatter(mValueAxis->coordToPixel(pointData->at(i).value), mKeyAxis->coordToPixel(pointData->at(i).key), mScatterSize, mScatterStyle);
1478 } else
1479 {
1480 for (int i=0; i<pointData->size(); ++i)
1481 painter->drawScatter(mKeyAxis->coordToPixel(pointData->at(i).key), mValueAxis->coordToPixel(pointData->at(i).value), mScatterSize, mScatterStyle);
1482 }
1483 }
1484
1485 /*!
1486 \internal
1487 Draws line graphs from the provided data. It connects all points in \a lineData, which
1488 was created by one of the "get(...)PlotData" functions for line styles that require simple line
1489 connections between the point vector they create. These are for example \ref getLinePlotData, \ref
1490 getStepLeftPlotData, \ref getStepRightPlotData and \ref getStepCenterPlotData.
1491 \see drawScatterPlot, drawImpulsePlot
1492 */
1493 void QCPGraph::drawLinePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
1494 {
1495 // draw line of graph:
1496 if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
1497 {
1498 applyDefaultAntialiasingHint(painter);
1499 painter->setPen(mainPen());
1500 painter->setBrush(Qt::NoBrush);
1501
1502 /* Draws polyline in batches, currently not used:
1503 int p = 0;
1504 while (p < lineData->size())
1505 {
1506 int batch = qMin(25, lineData->size()-p);
1507 if (p != 0)
1508 {
1509 ++batch;
1510 --p; // to draw the connection lines between two batches
1511 }
1512 painter->drawPolyline(lineData->constData()+p, batch);
1513 p += batch;
1514 }
1515 */
1516
1517 // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
1518 if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
1519 painter->pen().style() == Qt::SolidLine &&
1520 !painter->pdfExportMode())
1521 {
1522 for (int i=1; i<lineData->size(); ++i)
1523 painter->drawLine(lineData->at(i-1), lineData->at(i));
1524 } else
1525 {
1526 painter->drawPolyline(QPolygonF(*lineData));
1527 }
1528 }
1529 }
1530
1531 /*!
1532 \internal
1533 Draws impulses graphs from the provided data, i.e. it connects all line pairs in \a lineData, which was
1534 created by \ref getImpulsePlotData.
1535 \see drawScatterPlot, drawLinePlot
1536 */
1537 void QCPGraph::drawImpulsePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
1538 {
1539 // draw impulses:
1540 if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
1541 {
1542 applyDefaultAntialiasingHint(painter);
1543 QPen pen = mainPen();
1544 pen.setCapStyle(Qt::FlatCap); // so impulse line doesn't reach beyond zero-line
1545 painter->setPen(pen);
1546 painter->setBrush(Qt::NoBrush);
1547 painter->drawLines(*lineData);
1548 }
1549 }
1550
1551 /*!
1552 \internal
1553 called by the scatter drawing function (\ref drawScatterPlot) to draw the error bars on one data
1554 point. \a x and \a y pixel positions of the data point are passed since they are already known in
1555 pixel coordinates in the drawing function, so we save some extra coordToPixel transforms here. \a
1556 data is therefore only used for the errors, not key and value.
1557 */
1558 void QCPGraph::drawError(QCPPainter *painter, double x, double y, const QCPData &data) const
1559 {
1560 double a, b; // positions of error bar bounds in pixels
1561 double barWidthHalf = mErrorBarSize*0.5;
1562 double skipSymbolMargin = mScatterSize*1.25; // pixels left blank per side, when mErrorBarSkipSymbol is true
1563
1564 if (mKeyAxis->orientation() == Qt::Vertical)
1565 {
1566 // draw key error vertically and value error horizontally
1567 if (mErrorType == etKey || mErrorType == etBoth)
1568 {
1569 a = mKeyAxis->coordToPixel(data.key-data.keyErrorMinus);
1570 b = mKeyAxis->coordToPixel(data.key+data.keyErrorPlus);
1571 if (mKeyAxis->rangeReversed())
1572 qSwap(a,b);
1573 // draw spine:
1574 if (mErrorBarSkipSymbol)
1575 {
1576 if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
1577 painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
1578 if (y-b > skipSymbolMargin)
1579 painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
1580 } else
1581 painter->drawLine(QLineF(x, a, x, b));
1582 // draw handles:
1583 painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
1584 painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
1585 }
1586 if (mErrorType == etValue || mErrorType == etBoth)
1587 {
1588 a = mValueAxis->coordToPixel(data.value-data.valueErrorMinus);
1589 b = mValueAxis->coordToPixel(data.value+data.valueErrorPlus);
1590 if (mValueAxis->rangeReversed())
1591 qSwap(a,b);
1592 // draw spine:
1593 if (mErrorBarSkipSymbol)
1594 {
1595 if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
1596 painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
1597 if (b-x > skipSymbolMargin)
1598 painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
1599 } else
1600 painter->drawLine(QLineF(a, y, b, y));
1601 // draw handles:
1602 painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
1603 painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
1604 }
1605 } else // mKeyAxis->orientation() is Qt::Horizontal
1606 {
1607 // draw value error vertically and key error horizontally
1608 if (mErrorType == etKey || mErrorType == etBoth)
1609 {
1610 a = mKeyAxis->coordToPixel(data.key-data.keyErrorMinus);
1611 b = mKeyAxis->coordToPixel(data.key+data.keyErrorPlus);
1612 if (mKeyAxis->rangeReversed())
1613 qSwap(a,b);
1614 // draw spine:
1615 if (mErrorBarSkipSymbol)
1616 {
1617 if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
1618 painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
1619 if (b-x > skipSymbolMargin)
1620 painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
1621 } else
1622 painter->drawLine(QLineF(a, y, b, y));
1623 // draw handles:
1624 painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
1625 painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
1626 }
1627 if (mErrorType == etValue || mErrorType == etBoth)
1628 {
1629 a = mValueAxis->coordToPixel(data.value-data.valueErrorMinus);
1630 b = mValueAxis->coordToPixel(data.value+data.valueErrorPlus);
1631 if (mValueAxis->rangeReversed())
1632 qSwap(a,b);
1633 // draw spine:
1634 if (mErrorBarSkipSymbol)
1635 {
1636 if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
1637 painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
1638 if (y-b > skipSymbolMargin)
1639 painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
1640 } else
1641 painter->drawLine(QLineF(x, a, x, b));
1642 // draw handles:
1643 painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
1644 painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
1645 }
1646 }
1647 }
1648
1649 /*!
1650 \internal
1651 called by the specific plot data generating functions "get(...)PlotData" to determine
1652 which data range is visible, so only that needs to be processed.
1653
1654 \param[out] lower returns an iterator to the lowest data point that needs to be taken into account
1655 when plotting. Note that in order to get a clean plot all the way to the edge of the axes, \a lower
1656 may still be outside the visible range.
1657 \param[out] upper returns an iterator to the highest data point. Same as before, \a upper may also
1658 lie outside of the visible range.
1659 \param[out] count number of data points that need plotting, i.e. points between \a lower and \a upper,
1660 including them. This is useful for allocating the array of QPointFs in the specific drawing functions.
1661 */
1662 void QCPGraph::getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper, int &count) const
1663 {
1664 // get visible data range as QMap iterators
1665 QCPDataMap::const_iterator lbound = mData->lowerBound(mKeyAxis->range().lower);
1666 QCPDataMap::const_iterator ubound = mData->upperBound(mKeyAxis->range().upper)-1;
1667 bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range
1668 bool highoutlier = ubound+1 != mData->constEnd(); // indicates whether there exist points above axis range
1669 lower = (lowoutlier ? lbound-1 : lbound); // data pointrange that will be actually drawn
1670 upper = (highoutlier ? ubound+1 : ubound); // data pointrange that will be actually drawn
1671
1672 // count number of points in range lower to upper (including them), so we can allocate array for them in draw functions:
1673 QCPDataMap::const_iterator it = lower;
1674 count = 1;
1675 while (it != upper)
1676 {
1677 ++it;
1678 ++count;
1679 }
1680 }
1681
1682 /*!
1683 \internal
1684 The line data vector generated by e.g. getLinePlotData contains only the line
1685 that connects the data points. If the graph needs to be filled, two additional points
1686 need to be added at the value-zero-line in the lower and upper key positions, the graph
1687 reaches. This function calculates these points and adds them to the end of \a lineData.
1688 Since the fill is typically drawn before the line stroke, these added points need to
1689 be removed again after the fill is done, with the removeFillBasePoints function.
1690
1691 The expanding of \a lineData by two points will not cause unnecessary memory reallocations,
1692 because the data vector generation functions (getLinePlotData etc.) reserve two extra points
1693 when they allocate memory for \a lineData.
1694 \see removeFillBasePoints, lowerFillBasePoint, upperFillBasePoint
1695 */
1696 void QCPGraph::addFillBasePoints(QVector<QPointF> *lineData) const
1697 {
1698 // append points that close the polygon fill at the key axis:
1699 if (mKeyAxis->orientation() == Qt::Vertical)
1700 {
1701 *lineData << upperFillBasePoint(lineData->last().y());
1702 *lineData << lowerFillBasePoint(lineData->first().y());
1703 } else
1704 {
1705 *lineData << upperFillBasePoint(lineData->last().x());
1706 *lineData << lowerFillBasePoint(lineData->first().x());
1707 }
1708 }
1709
1710 /*!
1711 \internal
1712 removes the two points from \a lineData that were added by addFillBasePoints.
1713 \see addFillBasePoints, lowerFillBasePoint, upperFillBasePoint
1714 */
1715 void QCPGraph::removeFillBasePoints(QVector<QPointF> *lineData) const
1716 {
1717 lineData->remove(lineData->size()-2, 2);
1718 }
1719
1720 /*!
1721 \internal
1722 called by addFillBasePoints to conveniently assign the point which closes the fill
1723 polygon on the lower side of the zero-value-line parallel to the key axis.
1724 The logarithmic axis scale case is a bit special, since the zero-value-line in pixel coordinates
1725 is in positive or negative infinity. So this case is handled separately by just closing the
1726 fill polygon on the axis which lies in the direction towards the zero value.
1727
1728 \param lowerKey pixel position of the lower key of the point. Depending on whether the key axis
1729 is horizontal or vertical, \a lowerKey will end up as the x or y value of the returned point,
1730 respectively.
1731 \see upperFillBasePoint, addFillBasePoints
1732 */
1733 QPointF QCPGraph::lowerFillBasePoint(double lowerKey) const
1734 {
1735 QPointF point;
1736 if (mValueAxis->scaleType() == QCPAxis::stLinear)
1737 {
1738 if (mKeyAxis->axisType() == QCPAxis::atLeft)
1739 {
1740 point.setX(mValueAxis->coordToPixel(0));
1741 point.setY(lowerKey);
1742 } else if (mKeyAxis->axisType() == QCPAxis::atRight)
1743 {
1744 point.setX(mValueAxis->coordToPixel(0));
1745 point.setY(lowerKey);
1746 } else if (mKeyAxis->axisType() == QCPAxis::atTop)
1747 {
1748 point.setX(lowerKey);
1749 point.setY(mValueAxis->coordToPixel(0));
1750 } else if (mKeyAxis->axisType() == QCPAxis::atBottom)
1751 {
1752 point.setX(lowerKey);
1753 point.setY(mValueAxis->coordToPixel(0));
1754 }
1755 } else // mValueAxis->mScaleType == QCPAxis::stLogarithmic
1756 {
1757 // In logarithmic scaling we can't just draw to value zero so we just fill all the way
1758 // to the axis which is in the direction towards zero
1759 if (mKeyAxis->orientation() == Qt::Vertical)
1760 {
1761 if ((mValueAxis->range().upper < 0 && !mValueAxis->rangeReversed()) ||
1762 (mValueAxis->range().upper > 0 && mValueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
1763 point.setX(mKeyAxis->axisRect().right());
1764 else
1765 point.setX(mKeyAxis->axisRect().left());
1766 point.setY(lowerKey);
1767 } else if (mKeyAxis->axisType() == QCPAxis::atTop || mKeyAxis->axisType() == QCPAxis::atBottom)
1768 {
1769 point.setX(lowerKey);
1770 if ((mValueAxis->range().upper < 0 && !mValueAxis->rangeReversed()) ||
1771 (mValueAxis->range().upper > 0 && mValueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
1772 point.setY(mKeyAxis->axisRect().top());
1773 else
1774 point.setY(mKeyAxis->axisRect().bottom());
1775 }
1776 }
1777 return point;
1778 }
1779
1780 /*!
1781 \internal
1782 called by addFillBasePoints to conveniently assign the point which closes the fill
1783 polygon on the upper side of the zero-value-line parallel to the key axis. The logarithmic axis
1784 scale case is a bit special, since the zero-value-line in pixel coordinates is in positive or
1785 negative infinity. So this case is handled separately by just closing the fill polygon on the
1786 axis which lies in the direction towards the zero value.
1787
1788 \param upperKey pixel position of the upper key of the point. Depending on whether the key axis
1789 is horizontal or vertical, \a upperKey will end up as the x or y value of the returned point,
1790 respectively.
1791 \see lowerFillBasePoint, addFillBasePoints
1792 */
1793 QPointF QCPGraph::upperFillBasePoint(double upperKey) const
1794 {
1795 QPointF point;
1796 if (mValueAxis->scaleType() == QCPAxis::stLinear)
1797 {
1798 if (mKeyAxis->axisType() == QCPAxis::atLeft)
1799 {
1800 point.setX(mValueAxis->coordToPixel(0));
1801 point.setY(upperKey);
1802 } else if (mKeyAxis->axisType() == QCPAxis::atRight)
1803 {
1804 point.setX(mValueAxis->coordToPixel(0));
1805 point.setY(upperKey);
1806 } else if (mKeyAxis->axisType() == QCPAxis::atTop)
1807 {
1808 point.setX(upperKey);
1809 point.setY(mValueAxis->coordToPixel(0));
1810 } else if (mKeyAxis->axisType() == QCPAxis::atBottom)
1811 {
1812 point.setX(upperKey);
1813 point.setY(mValueAxis->coordToPixel(0));
1814 }
1815 } else // mValueAxis->mScaleType == QCPAxis::stLogarithmic
1816 {
1817 // In logarithmic scaling we can't just draw to value 0 so we just fill all the way
1818 // to the axis which is in the direction towards 0
1819 if (mKeyAxis->orientation() == Qt::Vertical)
1820 {
1821 if ((mValueAxis->range().upper < 0 && !mValueAxis->rangeReversed()) ||
1822 (mValueAxis->range().upper > 0 && mValueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
1823 point.setX(mKeyAxis->axisRect().right());
1824 else
1825 point.setX(mKeyAxis->axisRect().left());
1826 point.setY(upperKey);
1827 } else if (mKeyAxis->axisType() == QCPAxis::atTop || mKeyAxis->axisType() == QCPAxis::atBottom)
1828 {
1829 point.setX(upperKey);
1830 if ((mValueAxis->range().upper < 0 && !mValueAxis->rangeReversed()) ||
1831 (mValueAxis->range().upper > 0 && mValueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
1832 point.setY(mKeyAxis->axisRect().top());
1833 else
1834 point.setY(mKeyAxis->axisRect().bottom());
1835 }
1836 }
1837 return point;
1838 }
1839
1840 /*! \internal
1841
1842 Generates the polygon needed for drawing channel fills between this graph (data passed via \a
1843 lineData) and the graph specified by mChannelFillGraph (data generated by calling its \ref
1844 getPlotData function). May return an empty polygon if the key ranges have no overlap or fill
1845 target graph and this graph don't have same orientation (i.e. both key axes horizontal or both
1846 key axes vertical). For increased performance (due to implicit sharing), keep the returned QPolygonF
1847 const.
1848 */
1849 const QPolygonF QCPGraph::getChannelFillPolygon(const QVector<QPointF> *lineData) const
1850 {
1851 if (mChannelFillGraph->mKeyAxis->orientation() != mKeyAxis->orientation())
1852 return QPolygonF(); // don't have same axis orientation, can't fill that (Note: if keyAxis fits, valueAxis will fit too, because it's always orthogonal to keyAxis)
1853
1854 if (lineData->isEmpty()) return QPolygonF();
1855 QVector<QPointF> otherData;
1856 mChannelFillGraph->getPlotData(&otherData, 0);
1857 if (otherData.isEmpty()) return QPolygonF();
1858 QVector<QPointF> thisData;
1859 thisData.reserve(lineData->size()+otherData.size()); // because we will join both vectors at end of this function
1860 for (int i=0; i<lineData->size(); ++i) // don't use the vector<<(vector), it squeezes internally, which ruins the performance tuning with reserve()
1861 thisData << lineData->at(i);
1862
1863 // pointers to be able to swap them, depending which data range needs cropping:
1864 QVector<QPointF> *staticData = &thisData;
1865 QVector<QPointF> *croppedData = &otherData;
1866
1867 // crop both vectors to ranges in which the keys overlap (which coord is key, depends on axisType):
1868 if (mKeyAxis->orientation() == Qt::Horizontal)
1869 {
1870 // x is key
1871 // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
1872 if (staticData->first().x() > staticData->last().x())
1873 {
1874 int size = staticData->size();
1875 for (int i=0; i<size/2; ++i)
1876 qSwap((*staticData)[i], (*staticData)[size-1-i]);
1877 }
1878 if (croppedData->first().x() > croppedData->last().x())
1879 {
1880 int size = croppedData->size();
1881 for (int i=0; i<size/2; ++i)
1882 qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
1883 }
1884 // crop lower bound:
1885 if (staticData->first().x() < croppedData->first().x()) // other one must be cropped
1886 qSwap(staticData, croppedData);
1887 int lowBound = findIndexBelowX(croppedData, staticData->first().x());
1888 if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
1889 croppedData->remove(0, lowBound);
1890 // set lowest point of cropped data to fit exactly key position of first static data
1891 // point via linear interpolation:
1892 if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
1893 double slope;
1894 if (croppedData->at(1).x()-croppedData->at(0).x() != 0)
1895 slope = (croppedData->at(1).y()-croppedData->at(0).y())/(croppedData->at(1).x()-croppedData->at(0).x());
1896 else
1897 slope = 0;
1898 (*croppedData)[0].setY(croppedData->at(0).y()+slope*(staticData->first().x()-croppedData->at(0).x()));
1899 (*croppedData)[0].setX(staticData->first().x());
1900
1901 // crop upper bound:
1902 if (staticData->last().x() > croppedData->last().x()) // other one must be cropped
1903 qSwap(staticData, croppedData);
1904 int highBound = findIndexAboveX(croppedData, staticData->last().x());
1905 if (highBound == -1) return QPolygonF(); // key ranges have no overlap
1906 croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
1907 // set highest point of cropped data to fit exactly key position of last static data
1908 // point via linear interpolation:
1909 if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
1910 int li = croppedData->size()-1; // last index
1911 if (croppedData->at(li).x()-croppedData->at(li-1).x() != 0)
1912 slope = (croppedData->at(li).y()-croppedData->at(li-1).y())/(croppedData->at(li).x()-croppedData->at(li-1).x());
1913 else
1914 slope = 0;
1915 (*croppedData)[li].setY(croppedData->at(li-1).y()+slope*(staticData->last().x()-croppedData->at(li-1).x()));
1916 (*croppedData)[li].setX(staticData->last().x());
1917 } else // mKeyAxis->orientation() == Qt::Vertical
1918 {
1919 // y is key
1920 // similar to "x is key" but switched x,y. Further, lower/upper meaning is inverted compared to x,
1921 // because in pixel coordinates, y increases from top to bottom, not bottom to top like data coordinate.
1922 // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
1923 if (staticData->first().y() < staticData->last().y())
1924 {
1925 int size = staticData->size();
1926 for (int i=0; i<size/2; ++i)
1927 qSwap((*staticData)[i], (*staticData)[size-1-i]);
1928 }
1929 if (croppedData->first().y() < croppedData->last().y())
1930 {
1931 int size = croppedData->size();
1932 for (int i=0; i<size/2; ++i)
1933 qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
1934 }
1935 // crop lower bound:
1936 if (staticData->first().y() > croppedData->first().y()) // other one must be cropped
1937 qSwap(staticData, croppedData);
1938 int lowBound = findIndexAboveY(croppedData, staticData->first().y());
1939 if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
1940 croppedData->remove(0, lowBound);
1941 // set lowest point of cropped data to fit exactly key position of first static data
1942 // point via linear interpolation:
1943 if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
1944 double slope;
1945 if (croppedData->at(1).y()-croppedData->at(0).y() != 0) // avoid division by zero in step plots
1946 slope = (croppedData->at(1).x()-croppedData->at(0).x())/(croppedData->at(1).y()-croppedData->at(0).y());
1947 else
1948 slope = 0;
1949 (*croppedData)[0].setX(croppedData->at(0).x()+slope*(staticData->first().y()-croppedData->at(0).y()));
1950 (*croppedData)[0].setY(staticData->first().y());
1951
1952 // crop upper bound:
1953 if (staticData->last().y() < croppedData->last().y()) // other one must be cropped
1954 qSwap(staticData, croppedData);
1955 int highBound = findIndexBelowY(croppedData, staticData->last().y());
1956 if (highBound == -1) return QPolygonF(); // key ranges have no overlap
1957 croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
1958 // set highest point of cropped data to fit exactly key position of last static data
1959 // point via linear interpolation:
1960 if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
1961 int li = croppedData->size()-1; // last index
1962 if (croppedData->at(li).y()-croppedData->at(li-1).y() != 0) // avoid division by zero in step plots
1963 slope = (croppedData->at(li).x()-croppedData->at(li-1).x())/(croppedData->at(li).y()-croppedData->at(li-1).y());
1964 else
1965 slope = 0;
1966 (*croppedData)[li].setX(croppedData->at(li-1).x()+slope*(staticData->last().y()-croppedData->at(li-1).y()));
1967 (*croppedData)[li].setY(staticData->last().y());
1968 }
1969
1970 // return joined:
1971 for (int i=otherData.size()-1; i>=0; --i) // insert reversed, otherwise the polygon will be twisted
1972 thisData << otherData.at(i);
1973 return QPolygonF(thisData);
1974 }
1975
1976 /*! \internal
1977
1978 Finds the smallest index of \a data, whose points x value is just above \a x.
1979 Assumes x values in \a data points are ordered ascending, as is the case
1980 when plotting with horizontal key axis.
1981 Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
1982 */
1983 int QCPGraph::findIndexAboveX(const QVector<QPointF> *data, double x) const
1984 {
1985 for (int i=data->size()-1; i>=0; --i)
1986 {
1987 if (data->at(i).x() < x)
1988 {
1989 if (i<data->size()-1)
1990 return i+1;
1991 else
1992 return data->size()-1;
1993 }
1994 }
1995 return -1;
1996 }
1997
1998 /*! \internal
1999
2000 Finds the greatest index of \a data, whose points x value is just below \a x.
2001 Assumes x values in \a data points are ordered ascending, as is the case
2002 when plotting with horizontal key axis.
2003 Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
2004 */
2005 int QCPGraph::findIndexBelowX(const QVector<QPointF> *data, double x) const
2006 {
2007 for (int i=0; i<data->size(); ++i)
2008 {
2009 if (data->at(i).x() > x)
2010 {
2011 if (i>0)
2012 return i-1;
2013 else
2014 return 0;
2015 }
2016 }
2017 return -1;
2018 }
2019
2020 /*! \internal
2021
2022 Finds the smallest index of \a data, whose points y value is just above \a y.
2023 Assumes y values in \a data points are ordered descending, as is the case
2024 when plotting with vertical key axis.
2025 Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
2026 */
2027 int QCPGraph::findIndexAboveY(const QVector<QPointF> *data, double y) const
2028 {
2029 for (int i=0; i<data->size(); ++i)
2030 {
2031 if (data->at(i).y() < y)
2032 {
2033 if (i>0)
2034 return i-1;
2035 else
2036 return 0;
2037 }
2038 }
2039 return -1;
2040 }
2041
2042 /*! \internal
2043
2044 Calculates the (minimum) distance (in pixels) the graph's representation has from the given \a
2045 pixelPoint in pixels. This is used to determine whether the graph was clicked or not, e.g. in
2046 \ref selectTest.
2047 */
2048 double QCPGraph::pointDistance(const QPointF &pixelPoint) const
2049 {
2050 if (mData->isEmpty())
2051 {
2052 qDebug() << Q_FUNC_INFO << "requested point distance on graph" << mName << "without data";
2053 return 500;
2054 }
2055 if (mData->size() == 1)
2056 {
2057 QPointF dataPoint = coordsToPixels(mData->constBegin().key(), mData->constBegin().value().value);
2058 return QVector2D(dataPoint-pixelPoint).length();
2059 }
2060
2061 if (mLineStyle == lsNone && mScatterStyle == QCP::ssNone)
2062 return 500;
2063
2064 // calculate minimum distances to graph representation:
2065 if (mLineStyle == lsNone)
2066 {
2067 // no line displayed, only calculate distance to scatter points:
2068 QVector<QCPData> *pointData = new QVector<QCPData>;
2069 getScatterPlotData(pointData);
2070 double minDistSqr = std::numeric_limits<double>::max();
2071 QPointF ptA;
2072 QPointF ptB = coordsToPixels(pointData->at(0).key, pointData->at(0).value); // getScatterPlotData returns in plot coordinates, so transform to pixels
2073 for (int i=1; i<pointData->size(); ++i)
2074 {
2075 ptA = ptB;
2076 ptB = coordsToPixels(pointData->at(i).key, pointData->at(i).value);
2077 double currentDistSqr = distSqrToLine(ptA, ptB, pixelPoint);
2078 if (currentDistSqr < minDistSqr)
2079 minDistSqr = currentDistSqr;
2080 }
2081 delete pointData;
2082 return sqrt(minDistSqr);
2083 } else
2084 {
2085 // line displayed calculate distance to line segments:
2086 QVector<QPointF> *lineData = new QVector<QPointF>;
2087 getPlotData(lineData, 0); // unlike with getScatterPlotData we get pixel coordinates here
2088 double minDistSqr = std::numeric_limits<double>::max();
2089 if (mLineStyle == lsImpulse)
2090 {
2091 // impulse plot differs from other line styles in that the lineData points are only pairwise connected:
2092 for (int i=0; i<lineData->size()-1; i+=2) // iterate pairs
2093 {
2094 double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
2095 if (currentDistSqr < minDistSqr)
2096 minDistSqr = currentDistSqr;
2097 }
2098 } else
2099 {
2100 // all other line plots (line and step) connect points directly:
2101 for (int i=0; i<lineData->size()-1; ++i)
2102 {
2103 double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
2104 if (currentDistSqr < minDistSqr)
2105 minDistSqr = currentDistSqr;
2106 }
2107 }
2108 delete lineData;
2109 return sqrt(minDistSqr);
2110 }
2111 }
2112
2113 /*! \internal
2114
2115 Finds the greatest index of \a data, whose points y value is just below \a y.
2116 Assumes y values in \a data points are ordered descending, as is the case
2117 when plotting with vertical key axis (since keys are ordered ascending).
2118 Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
2119 */
2120 int QCPGraph::findIndexBelowY(const QVector<QPointF> *data, double y) const
2121 {
2122 for (int i=data->size()-1; i>=0; --i)
2123 {
2124 if (data->at(i).y() > y)
2125 {
2126 if (i<data->size()-1)
2127 return i+1;
2128 else
2129 return data->size()-1;
2130 }
2131 }
2132 return -1;
2133 }
2134
2135 /* inherits documentation from base class */
2136 QCPRange QCPGraph::getKeyRange(bool &validRange, SignDomain inSignDomain) const
2137 {
2138 // just call the specialized version which takes an additional argument whether error bars
2139 // should also be taken into consideration for range calculation. We set this to true here.
2140 return getKeyRange(validRange, inSignDomain, true);
2141 }
2142
2143 /* inherits documentation from base class */
2144 QCPRange QCPGraph::getValueRange(bool &validRange, SignDomain inSignDomain) const
2145 {
2146 // just call the specialized version which takes an additional argument whether error bars
2147 // should also be taken into consideration for range calculation. We set this to true here.
2148 return getValueRange(validRange, inSignDomain, true);
2149 }
2150
2151 /*! \overload
2152 Allows to specify whether the error bars should be included in the range calculation.
2153
2154 \see getKeyRange(bool &validRange, SignDomain inSignDomain)
2155 */
2156 QCPRange QCPGraph::getKeyRange(bool &validRange, SignDomain inSignDomain, bool includeErrors) const
2157 {
2158 QCPRange range;
2159 bool haveLower = false;
2160 bool haveUpper = false;
2161
2162 double current, currentErrorMinus, currentErrorPlus;
2163
2164 if (inSignDomain == sdBoth) // range may be anywhere
2165 {
2166 QCPDataMap::const_iterator it = mData->constBegin();
2167 while (it != mData->constEnd())
2168 {
2169 current = it.value().key;
2170 currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
2171 currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
2172 if (current-currentErrorMinus < range.lower || !haveLower)
2173 {
2174 range.lower = current-currentErrorMinus;
2175 haveLower = true;
2176 }
2177 if (current+currentErrorPlus > range.upper || !haveUpper)
2178 {
2179 range.upper = current+currentErrorPlus;
2180 haveUpper = true;
2181 }
2182 ++it;
2183 }
2184 } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
2185 {
2186 QCPDataMap::const_iterator it = mData->constBegin();
2187 while (it != mData->constEnd())
2188 {
2189 current = it.value().key;
2190 currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
2191 currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
2192 if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
2193 {
2194 range.lower = current-currentErrorMinus;
2195 haveLower = true;
2196 }
2197 if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
2198 {
2199 range.upper = current+currentErrorPlus;
2200 haveUpper = true;
2201 }
2202 if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
2203 {
2204 if ((current < range.lower || !haveLower) && current < 0)
2205 {
2206 range.lower = current;
2207 haveLower = true;
2208 }
2209 if ((current > range.upper || !haveUpper) && current < 0)
2210 {
2211 range.upper = current;
2212 haveUpper = true;
2213 }
2214 }
2215 ++it;
2216 }
2217 } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
2218 {
2219 QCPDataMap::const_iterator it = mData->constBegin();
2220 while (it != mData->constEnd())
2221 {
2222 current = it.value().key;
2223 currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
2224 currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
2225 if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
2226 {
2227 range.lower = current-currentErrorMinus;
2228 haveLower = true;
2229 }
2230 if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
2231 {
2232 range.upper = current+currentErrorPlus;
2233 haveUpper = true;
2234 }
2235 if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
2236 {
2237 if ((current < range.lower || !haveLower) && current > 0)
2238 {
2239 range.lower = current;
2240 haveLower = true;
2241 }
2242 if ((current > range.upper || !haveUpper) && current > 0)
2243 {
2244 range.upper = current;
2245 haveUpper = true;
2246 }
2247 }
2248 ++it;
2249 }
2250 }
2251
2252 validRange = haveLower && haveUpper;
2253 return range;
2254 }
2255
2256 /*! \overload
2257 Allows to specify whether the error bars should be included in the range calculation.
2258
2259 \see getValueRange(bool &validRange, SignDomain inSignDomain)
2260 */
2261 QCPRange QCPGraph::getValueRange(bool &validRange, SignDomain inSignDomain, bool includeErrors) const
2262 {
2263 QCPRange range;
2264 bool haveLower = false;
2265 bool haveUpper = false;
2266
2267 double current, currentErrorMinus, currentErrorPlus;
2268
2269 if (inSignDomain == sdBoth) // range may be anywhere
2270 {
2271 QCPDataMap::const_iterator it = mData->constBegin();
2272 while (it != mData->constEnd())
2273 {
2274 current = it.value().value;
2275 currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
2276 currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
2277 if (current-currentErrorMinus < range.lower || !haveLower)
2278 {
2279 range.lower = current-currentErrorMinus;
2280 haveLower = true;
2281 }
2282 if (current+currentErrorPlus > range.upper || !haveUpper)
2283 {
2284 range.upper = current+currentErrorPlus;
2285 haveUpper = true;
2286 }
2287 ++it;
2288 }
2289 } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
2290 {
2291 QCPDataMap::const_iterator it = mData->constBegin();
2292 while (it != mData->constEnd())
2293 {
2294 current = it.value().value;
2295 currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
2296 currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
2297 if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
2298 {
2299 range.lower = current-currentErrorMinus;
2300 haveLower = true;
2301 }
2302 if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
2303 {
2304 range.upper = current+currentErrorPlus;
2305 haveUpper = true;
2306 }
2307 if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
2308 {
2309 if ((current < range.lower || !haveLower) && current < 0)
2310 {
2311 range.lower = current;
2312 haveLower = true;
2313 }
2314 if ((current > range.upper || !haveUpper) && current < 0)
2315 {
2316 range.upper = current;
2317 haveUpper = true;
2318 }
2319 }
2320 ++it;
2321 }
2322 } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
2323 {
2324 QCPDataMap::const_iterator it = mData->constBegin();
2325 while (it != mData->constEnd())
2326 {
2327 current = it.value().value;
2328 currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
2329 currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
2330 if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
2331 {
2332 range.lower = current-currentErrorMinus;
2333 haveLower = true;
2334 }
2335 if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
2336 {
2337 range.upper = current+currentErrorPlus;
2338 haveUpper = true;
2339 }
2340 if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
2341 {
2342 if ((current < range.lower || !haveLower) && current > 0)
2343 {
2344 range.lower = current;
2345 haveLower = true;
2346 }
2347 if ((current > range.upper || !haveUpper) && current > 0)
2348 {
2349 range.upper = current;
2350 haveUpper = true;
2351 }
2352 }
2353 ++it;
2354 }
2355 }
2356
2357 validRange = haveLower && haveUpper;
2358 return range;
2359 }
2360
2361
2362 // ================================================================================
2363 // =================== QCPRange
2364 // ================================================================================
2365 /*! \class QCPRange
2366 \brief Represents the range an axis is encompassing.
2367
2368 contains a \a lower and \a upper double value and provides convenience input, output and
2369 modification functions.
2370
2371 \see QCPAxis::setRange
2372 */
2373
2374 /*!
2375 Minimum range size (\a upper - \a lower) the range changing functions will accept. Smaller
2376 intervals would cause errors due to the 11-bit exponent of double precision numbers,
2377 corresponding to a minimum magnitude of roughly 1e-308.
2378 \see validRange, maxRange
2379 */
2380 const double QCPRange::minRange = 1e-280;
2381
2382 /*!
2383 Maximum values (negative and positive) the range will accept in range-changing functions.
2384 Larger absolute values would cause errors due to the 11-bit exponent of double precision numbers,
2385 corresponding to a maximum magnitude of roughly 1e308.
2386 Since the number of planck-volumes in the entire visible universe is only ~1e183, this should
2387 be enough.
2388 \see validRange, minRange
2389 */
2390 const double QCPRange::maxRange = 1e250;
2391
2392 /*!
2393 Constructs a range with \a lower and \a upper set to zero.
2394 */
2395 QCPRange::QCPRange() :
2396 lower(0),
2397 upper(0)
2398 {
2399 }
2400
2401 /*! \overload
2402 Constructs a range with the specified \a lower and \a upper values.
2403 */
2404 QCPRange::QCPRange(double lower, double upper) :
2405 lower(lower),
2406 upper(upper)
2407 {
2408 normalize();
2409 }
2410
2411 /*!
2412 Returns the size of the range, i.e. \a upper-\a lower
2413 */
2414 double QCPRange::size() const
2415 {
2416 return upper-lower;
2417 }
2418
2419 /*!
2420 Returns the center of the range, i.e. (\a upper+\a lower)*0.5
2421 */
2422 double QCPRange::center() const
2423 {
2424 return (upper+lower)*0.5;
2425 }
2426
2427 /*!
2428 Makes sure \a lower is numerically smaller than \a upper. If this is not the case, the values
2429 are swapped.
2430 */
2431 void QCPRange::normalize()
2432 {
2433 if (lower > upper)
2434 qSwap(lower, upper);
2435 }
2436
2437 /*!
2438 Returns a sanitized version of the range. Sanitized means for logarithmic scales, that
2439 the range won't span the positive and negative sign domain, i.e. contain zero. Further
2440 \a lower will always be numerically smaller (or equal) to \a upper.
2441
2442 If the original range does span positive and negative sign domains or contains zero,
2443 the returned range will try to approximate the original range as good as possible.
2444 If the positive interval of the original range is wider than the negative interval, the
2445 returned range will only contain the positive interval, with lower bound set to \a rangeFac or
2446 \a rangeFac *\a upper, whichever is closer to zero. Same procedure is used if the negative interval
2447 is wider than the positive interval, this time by changing the \a upper bound.
2448 */
2449 QCPRange QCPRange::sanitizedForLogScale() const
2450 {
2451 double rangeFac = 1e-3;
2452 QCPRange sanitizedRange(lower, upper);
2453 sanitizedRange.normalize();
2454 // can't have range spanning negative and positive values in log plot, so change range to fix it
2455 //if (qFuzzyCompare(sanitizedRange.lower+1, 1) && !qFuzzyCompare(sanitizedRange.upper+1, 1))
2456 if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0)
2457 {
2458 // case lower is 0
2459 if (rangeFac < sanitizedRange.upper*rangeFac)
2460 sanitizedRange.lower = rangeFac;
2461 else
2462 sanitizedRange.lower = sanitizedRange.upper*rangeFac;
2463 } //else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))
2464 else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0)
2465 {
2466 // case upper is 0
2467 if (-rangeFac > sanitizedRange.lower*rangeFac)
2468 sanitizedRange.upper = -rangeFac;
2469 else
2470 sanitizedRange.upper = sanitizedRange.lower*rangeFac;
2471 } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0)
2472 {
2473 // find out whether negative or positive interval is wider to decide which sign domain will be chosen
2474 if (-sanitizedRange.lower > sanitizedRange.upper)
2475 {
2476 // negative is wider, do same as in case upper is 0
2477 if (-rangeFac > sanitizedRange.lower*rangeFac)
2478 sanitizedRange.upper = -rangeFac;
2479 else
2480 sanitizedRange.upper = sanitizedRange.lower*rangeFac;
2481 } else
2482 {
2483 // positive is wider, do same as in case lower is 0
2484 if (rangeFac < sanitizedRange.upper*rangeFac)
2485 sanitizedRange.lower = rangeFac;
2486 else
2487 sanitizedRange.lower = sanitizedRange.upper*rangeFac;
2488 }
2489 }
2490 // due to normalization, case lower>0 && upper<0 should never occur, because that implies upper<lower
2491 return sanitizedRange;
2492 }
2493
2494 /*!
2495 Returns a sanitized version of the range. Sanitized means for linear scales, that
2496 \a lower will always be numerically smaller (or equal) to \a upper.
2497 */
2498 QCPRange QCPRange::sanitizedForLinScale() const
2499 {
2500 QCPRange sanitizedRange(lower, upper);
2501 sanitizedRange.normalize();
2502 return sanitizedRange;
2503 }
2504
2505 /*!
2506 Returns true when \a value lies within or exactly on the borders of the range.
2507 */
2508 bool QCPRange::contains(double value) const
2509 {
2510 return value >= lower && value <= upper;
2511 }
2512
2513 /*!
2514 Checks, whether the specified range is within valid bounds, which are defined
2515 as QCPRange::maxRange and QCPRange::minRange.
2516 A valid range means:
2517 \li range bounds within -maxRange and maxRange
2518 \li range size above minRange
2519 \li range size below maxRange
2520 */
2521 bool QCPRange::validRange(double lower, double upper)
2522 {
2523 /*
2524 return (lower > -maxRange &&
2525 upper < maxRange &&
2526 qAbs(lower-upper) > minRange &&
2527 (lower < -minRange || lower > minRange) &&
2528 (upper < -minRange || upper > minRange));
2529 */
2530 return (lower > -maxRange &&
2531 upper < maxRange &&
2532 qAbs(lower-upper) > minRange &&
2533 qAbs(lower-upper) < maxRange);
2534 }
2535
2536 /*!
2537 \overload
2538 Checks, whether the specified range is within valid bounds, which are defined
2539 as QCPRange::maxRange and QCPRange::minRange.
2540 A valid range means:
2541 \li range bounds within -maxRange and maxRange
2542 \li range size above minRange
2543 \li range size below maxRange
2544 */
2545 bool QCPRange::validRange(const QCPRange &range)
2546 {
2547 /*
2548 return (range.lower > -maxRange &&
2549 range.upper < maxRange &&
2550 qAbs(range.lower-range.upper) > minRange &&
2551 qAbs(range.lower-range.upper) < maxRange &&
2552 (range.lower < -minRange || range.lower > minRange) &&
2553 (range.upper < -minRange || range.upper > minRange));
2554 */
2555 return (range.lower > -maxRange &&
2556 range.upper < maxRange &&
2557 qAbs(range.lower-range.upper) > minRange &&
2558 qAbs(range.lower-range.upper) < maxRange);
2559 }
2560
2561
2562 // ================================================================================
2563 // =================== QCPLegend
2564 // ================================================================================
2565
2566 /*! \class QCPLegend
2567 \brief Manages a legend inside a QCustomPlot.
2568
2569 Doesn't need to be instantiated externally, rather access QCustomPlot::legend
2570 */
2571
2572 /* start of documentation of signals */
2573
2574 /*! \fn void QCPLegend::selectionChanged(QCPLegend::SelectableParts selection);
2575
2576 This signal is emitted when the selection state of this legend has changed.
2577
2578 \see setSelected, setSelectable
2579 */
2580
2581 /* end of documentation of signals */
2582
2583 /*!
2584 Constructs a new QCPLegend instance with \a parentPlot as the containing plot and default
2585 values. Under normal usage, QCPLegend needn't be instantiated outside of QCustomPlot.
2586 Access QCustomPlot::legend to modify the legend (set to invisible by default, see \ref
2587 setVisible).
2588 */
2589 QCPLegend::QCPLegend(QCustomPlot *parentPlot) :
2590 QCPLayerable(parentPlot)
2591 {
2592 setAntialiased(false);
2593 setPositionStyle(psTopRight);
2594 setSize(100, 28);
2595 setMinimumSize(100, 0);
2596 setIconSize(32, 18);
2597 setAutoSize(true);
2598
2599 setMargin(12, 12, 12, 12);
2600 setPadding(8, 8, 3, 3);
2601 setItemSpacing(3);
2602 setIconTextPadding(7);
2603
2604 setSelectable(spLegendBox | spItems);
2605 setSelected(spNone);
2606
2607 setBorderPen(QPen(Qt::black));
2608 setSelectedBorderPen(QPen(Qt::blue, 2));
2609 setIconBorderPen(Qt::NoPen);
2610 setSelectedIconBorderPen(QPen(Qt::blue, 2));
2611 setBrush(Qt::white);
2612 setSelectedBrush(Qt::white);
2613 setFont(parentPlot->font());
2614 setSelectedFont(parentPlot->font());
2615 setTextColor(Qt::black);
2616 setSelectedTextColor(Qt::blue);
2617 }
2618
2619 QCPLegend::~QCPLegend()
2620 {
2621 clearItems();
2622 }
2623
2624 /*!
2625 Sets the pen, the border of the entire legend is drawn with.
2626 */
2627 void QCPLegend::setBorderPen(const QPen &pen)
2628 {
2629 mBorderPen = pen;
2630 }
2631
2632 /*!
2633 Sets the brush of the legend background.
2634 */
2635 void QCPLegend::setBrush(const QBrush &brush)
2636 {
2637 mBrush = brush;
2638 }
2639
2640 /*!
2641 Sets the default font of legend text. Legend items that draw text (e.g. the name of a graph) will
2642 use this font by default. However, a different font can be specified on a per-item-basis by
2643 accessing the specific legend item.
2644
2645 This function will also set \a font on all already existing legend items.
2646
2647 \see QCPAbstractLegendItem::setFont
2648 */
2649 void QCPLegend::setFont(const QFont &font)
2650 {
2651 mFont = font;
2652 for (int i=0; i<mItems.size(); ++i)
2653 mItems.at(i)->setFont(mFont);
2654 }
2655
2656 /*!
2657 Sets the default color of legend text. Legend items that draw text (e.g. the name of a graph)
2658 will use this color by default. However, a different colors can be specified on a per-item-basis
2659 by accessing the specific legend item.
2660
2661 This function will also set \a color on all already existing legend items.
2662
2663 \see QCPAbstractLegendItem::setTextColor
2664 */
2665 void QCPLegend::setTextColor(const QColor &color)
2666 {
2667 mTextColor = color;
2668 for (int i=0; i<mItems.size(); ++i)
2669 mItems.at(i)->setTextColor(color);
2670 }
2671
2672 /*!
2673 Sets the position style of the legend. If the \a legendPositionStyle is not \ref psManual, the
2674 position is found automatically depending on the specific \a legendPositionStyle and the
2675 legend margins. If \a legendPositionStyle is \ref psManual, the exact pixel position of the
2676 legend must be specified via \ref setPosition. Margins have no effect in that case.
2677 \see setMargin
2678 */
2679 void QCPLegend::setPositionStyle(PositionStyle legendPositionStyle)
2680 {
2681 mPositionStyle = legendPositionStyle;
2682 }
2683
2684 /*!
2685 Sets the exact pixel Position of the legend inside the QCustomPlot widget, if \ref
2686 setPositionStyle is set to \ref psManual. Margins have no effect in that case.
2687 */
2688 void QCPLegend::setPosition(const QPoint &pixelPosition)
2689 {
2690 mPosition = pixelPosition;
2691 }
2692
2693 /*!
2694 Sets whether the size of the legend should be calculated automatically to fit all the content
2695 (plus padding), or whether the size must be specified manually with \ref setSize.
2696
2697 If the autoSize mechanism is enabled, the legend will have the smallest possible size to still
2698 display all its content. For items with text wrapping (QCPPlottableLegendItem::setTextWrap) this
2699 means, they would become very compressed, i.e. wrapped at every word. To prevent this, set a
2700 reasonable \ref setMinimumSize width.
2701 */
2702 void QCPLegend::setAutoSize(bool on)
2703 {
2704 mAutoSize = on;
2705 }
2706
2707 /*!
2708 Sets the size of the legend. Setting the size manually with this function only has an effect, if
2709 \ref setAutoSize is set to false.
2710
2711 If you want to control the minimum size (or the text-wrapping width) while still leaving the
2712 autoSize mechanism enabled, consider using \ref setMinimumSize.
2713
2714 \see setAutoSize, setMinimumSize
2715 */
2716 void QCPLegend::setSize(const QSize &size)
2717 {
2718 mSize = size;
2719 }
2720
2721 /*! \overload
2722 */
2723 void QCPLegend::setSize(int width, int height)
2724 {
2725 mSize = QSize(width, height);
2726 }
2727
2728 /*!
2729 Sets the minimum size of the legend when \ref setAutoSize is enabled.
2730
2731 If text wrapping is enabled in the legend items (e.g. \ref QCPPlottableLegendItem::setTextWrap), this minimum \a size defines the width
2732 at which the wrapping will occur. Note that the wrapping will happen only at word boundaries, so the actual size might
2733 still be bigger than the \a size given here, but not smaller.
2734
2735 If \ref setAutoSize is not enabled, the minimum \a size is ignored. Setting a smaller legend size with \ref setSize manually, is not prevented.
2736
2737 \see setAutoSize, setSize, QCPPlottableLegendItem::setTextWrap
2738 */
2739 void QCPLegend::setMinimumSize(const QSize &size)
2740 {
2741 mMinimumSize = size;
2742 }
2743
2744 /*! \overload
2745 */
2746 void QCPLegend::setMinimumSize(int width, int height)
2747 {
2748 mMinimumSize = QSize(width, height);
2749 }
2750
2751 /*!
2752 Sets the left padding of the legend. Padding is the space by what the legend box is made larger
2753 than minimally needed for the content to fit. I.e. it's the space left blank on each side inside
2754 the legend.
2755 */
2756 void QCPLegend::setPaddingLeft(int padding)
2757 {
2758 mPaddingLeft = padding;
2759 }
2760
2761 /*!
2762 Sets the right padding of the legend. Padding is the space by what the legend box is made larger
2763 than minimally needed for the content to fit. I.e. it's the space left blank on each side inside
2764 the legend.
2765 */
2766 void QCPLegend::setPaddingRight(int padding)
2767 {
2768 mPaddingRight = padding;
2769 }
2770
2771 /*!
2772 Sets the top padding of the legend. Padding is the space by what the legend box is made larger
2773 than minimally needed for the content to fit. I.e. it's the space left blank on each side inside
2774 the legend.
2775 */
2776 void QCPLegend::setPaddingTop(int padding)
2777 {
2778 mPaddingTop = padding;
2779 }
2780
2781 /*!
2782 Sets the bottom padding of the legend. Padding is the space by what the legend box is made larger
2783 than minimally needed for the content to fit. I.e. it's the space left blank on each side inside
2784 the legend.
2785 */
2786 void QCPLegend::setPaddingBottom(int padding)
2787 {
2788 mPaddingBottom = padding;
2789 }
2790
2791 /*!
2792 Sets the padding of the legend. Padding is the space by what the legend box is made larger than
2793 minimally needed for the content to fit. I.e. it's the space left blank on each side inside the
2794 legend.
2795 */
2796 void QCPLegend::setPadding(int left, int right, int top, int bottom)
2797 {
2798 mPaddingLeft = left;
2799 mPaddingRight = right;
2800 mPaddingTop = top;
2801 mPaddingBottom = bottom;
2802 }
2803
2804 /*!
2805 Sets the left margin of the legend. Margins are the distances the legend will keep to the axis
2806 rect, when \ref setPositionStyle is not \ref psManual.
2807 */
2808 void QCPLegend::setMarginLeft(int margin)
2809 {
2810 mMarginLeft = margin;
2811 }
2812
2813 /*!
2814 Sets the right margin of the legend. Margins are the distances the legend will keep to the axis
2815 rect, when \ref setPositionStyle is not \ref psManual.
2816 */
2817 void QCPLegend::setMarginRight(int margin)
2818 {
2819 mMarginRight = margin;
2820 }
2821
2822 /*!
2823 Sets the top margin of the legend. Margins are the distances the legend will keep to the axis
2824 rect, when \ref setPositionStyle is not \ref psManual.
2825 */
2826 void QCPLegend::setMarginTop(int margin)
2827 {
2828 mMarginTop = margin;
2829 }
2830
2831 /*!
2832 Sets the bottom margin of the legend. Margins are the distances the legend will keep to the axis
2833 rect, when \ref setPositionStyle is not \ref psManual.
2834 */
2835 void QCPLegend::setMarginBottom(int margin)
2836 {
2837 mMarginBottom = margin;
2838 }
2839
2840 /*!
2841 Sets the margin of the legend. Margins are the distances the legend will keep to the axis rect,
2842 when \ref setPositionStyle is not \ref psManual.
2843 */
2844 void QCPLegend::setMargin(int left, int right, int top, int bottom)
2845 {
2846 mMarginLeft = left;
2847 mMarginRight = right;
2848 mMarginTop = top;
2849 mMarginBottom = bottom;
2850 }
2851
2852 /*!
2853 Sets the vertical space between two legend items in the legend.
2854
2855 \see setIconTextPadding, setPadding
2856 */
2857 void QCPLegend::setItemSpacing(int spacing)
2858 {
2859 mItemSpacing = spacing;
2860 }
2861
2862 /*!
2863 Sets the size of legend icons. Legend items that draw an icon (e.g. a visual
2864 representation of the graph) will use this size by default.
2865 */
2866 void QCPLegend::setIconSize(const QSize &size)
2867 {
2868 mIconSize = size;
2869 }
2870
2871 /*! \overload
2872 */
2873 void QCPLegend::setIconSize(int width, int height)
2874 {
2875 mIconSize.setWidth(width);
2876 mIconSize.setHeight(height);
2877 }
2878
2879 /*!
2880 Sets the horizontal space in pixels between the legend icon and the text next to it.
2881 Legend items that draw an icon (e.g. a visual representation of the graph) and text (e.g. the
2882 name of the graph) will use this space by default.
2883
2884 \see setItemSpacing
2885 */
2886 void QCPLegend::setIconTextPadding(int padding)
2887 {
2888 mIconTextPadding = padding;
2889 }
2890
2891 /*!
2892 Sets the pen used to draw a border around each legend icon. Legend items that draw an
2893 icon (e.g. a visual representation of the graph) will use this pen by default.
2894
2895 If no border is wanted, set this to \a Qt::NoPen.
2896 */
2897 void QCPLegend::setIconBorderPen(const QPen &pen)
2898 {
2899 mIconBorderPen = pen;
2900 }
2901
2902 /*!
2903 Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface.
2904 (When \ref QCustomPlot::setInteractions contains iSelectLegend.)
2905
2906 However, even when \a selectable is set to a value not allowing the selection of a specific part,
2907 it is still possible to set the selection of this part manually, by calling \ref setSelected
2908 directly.
2909
2910 \see SelectablePart, setSelected
2911 */
2912 void QCPLegend::setSelectable(const SelectableParts &selectable)
2913 {
2914 mSelectable = selectable;
2915 }
2916
2917 /*!
2918 Sets the selected state of the respective legend parts described by \ref SelectablePart. When a part
2919 is selected, it uses a different pen/font and brush. If some legend items are selected and \a selected
2920 doesn't contain \ref spItems, those items become deselected.
2921
2922 The entire selection mechanism is handled automatically when \ref QCustomPlot::setInteractions
2923 contains iSelectLegend. You only need to call this function when you wish to change the selection
2924 state manually.
2925
2926 This function can change the selection state of a part even when \ref setSelectable was set to a
2927 value that actually excludes the part.
2928
2929 emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
2930
2931 Note that it doesn't make sense to set the selected state \ref spItems here when it wasn't set
2932 before, because there's no way to specify which exact items to newly select. Do this by calling
2933 \ref QCPAbstractLegendItem::setSelected directly on the legend item you wish to select.
2934
2935 \see SelectablePart, setSelectable, selectTest, setSelectedBorderPen, setSelectedIconBorderPen, setSelectedBrush,
2936 setSelectedFont
2937 */
2938 void QCPLegend::setSelected(const SelectableParts &selected)
2939 {
2940 if (mSelected != selected)
2941 {
2942 if (!selected.testFlag(spItems) && mSelected.testFlag(spItems)) // some items are selected, but new selection state doesn't contain spItems, so deselect them
2943 {
2944 for (int i=0; i<mItems.size(); ++i)
2945 mItems.at(i)->setSelected(false);
2946 mSelected = selected;
2947 // not necessary to emit selectionChanged here because this will have happened for the last setSelected(false) on mItems already, via updateSelectionState()
2948 } else
2949 {
2950 mSelected = selected;
2951 emit selectionChanged(mSelected);
2952 }
2953 }
2954 }
2955
2956 /*!
2957 When the legend box is selected, this pen is used to draw the border instead of the normal pen
2958 set via \ref setBorderPen.
2959
2960 \see setSelected, setSelectable, setSelectedBrush
2961 */
2962 void QCPLegend::setSelectedBorderPen(const QPen &pen)
2963 {
2964 mSelectedBorderPen = pen;
2965 }
2966
2967 /*!
2968 Sets the pen legend items will use to draw their icon borders, when they are selected.
2969
2970 \see setSelected, setSelectable, setSelectedFont
2971 */
2972 void QCPLegend::setSelectedIconBorderPen(const QPen &pen)
2973 {
2974 mSelectedIconBorderPen = pen;
2975 }
2976
2977 /*!
2978 When the legend box is selected, this brush is used to draw the legend background instead of the normal brush
2979 set via \ref setBrush.
2980
2981 \see setSelected, setSelectable, setSelectedBorderPen
2982 */
2983 void QCPLegend::setSelectedBrush(const QBrush &brush)
2984 {
2985 mSelectedBrush = brush;
2986 }
2987
2988 /*!
2989 Sets the default font that is used by legend items when they are selected.
2990
2991 This function will also set \a font on all already existing legend items.
2992
2993 \see setFont, QCPAbstractLegendItem::setSelectedFont
2994 */
2995 void QCPLegend::setSelectedFont(const QFont &font)
2996 {
2997 mSelectedFont = font;
2998 for (int i=0; i<mItems.size(); ++i)
2999 mItems.at(i)->setSelectedFont(font);
3000 }
3001
3002 /*!
3003 Sets the default text color that is used by legend items when they are selected.
3004
3005 This function will also set \a color on all already existing legend items.
3006
3007 \see setTextColor, QCPAbstractLegendItem::setSelectedTextColor
3008 */
3009 void QCPLegend::setSelectedTextColor(const QColor &color)
3010 {
3011 mSelectedTextColor = color;
3012 for (int i=0; i<mItems.size(); ++i)
3013 mItems.at(i)->setSelectedTextColor(color);
3014 }
3015
3016 /*!
3017 Returns the item with index \a i.
3018
3019 \see itemCount
3020 */
3021 QCPAbstractLegendItem *QCPLegend::item(int index) const
3022 {
3023 if (index >= 0 && index < mItems.size())
3024 return mItems[index];
3025 else
3026 return 0;
3027 }
3028
3029 /*!
3030 Returns the QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
3031 If such an item isn't in the legend, returns 0.
3032
3033 \see hasItemWithPlottable
3034 */
3035 QCPPlottableLegendItem *QCPLegend::itemWithPlottable(const QCPAbstractPlottable *plottable) const
3036 {
3037 for (int i=0; i<mItems.size(); ++i)
3038 {
3039 if (QCPPlottableLegendItem *pli = qobject_cast<QCPPlottableLegendItem*>(mItems.at(i)))
3040 {
3041 if (pli->plottable() == plottable)
3042 return pli;
3043 }
3044 }
3045 return 0;
3046 }
3047
3048 /*!
3049 Returns the number of items currently in the legend.
3050 \see item
3051 */
3052 int QCPLegend::itemCount() const
3053 {
3054 return mItems.size();
3055 }
3056
3057 /*!
3058 Returns whether the legend contains \a item.
3059 */
3060 bool QCPLegend::hasItem(QCPAbstractLegendItem *item) const
3061 {
3062 return mItems.contains(item);
3063 }
3064
3065 /*!
3066 Returns whether the legend contains a QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
3067 If such an item isn't in the legend, returns false.
3068
3069 \see itemWithPlottable
3070 */
3071 bool QCPLegend::hasItemWithPlottable(const QCPAbstractPlottable *plottable) const
3072 {
3073 return itemWithPlottable(plottable);
3074 }
3075
3076 /*!
3077 Adds \a item to the legend, if it's not present already.
3078
3079 Returns true on sucess, i.e. if the item wasn't in the list already and has been successfuly added.
3080
3081 The legend takes ownership of the item.
3082 */
3083 bool QCPLegend::addItem(QCPAbstractLegendItem *item)
3084 {
3085 if (!mItems.contains(item))
3086 {
3087 mItems.append(item);
3088 return true;
3089 } else
3090 return false;
3091 }
3092
3093 /*!
3094 Removes the item with index \a index from the legend.
3095
3096 Returns true, if successful.
3097
3098 \see itemCount, clearItems
3099 */
3100 bool QCPLegend::removeItem(int index)
3101 {
3102 if (index >= 0 && index < mItems.size())
3103 {
3104 mItemBoundingBoxes.remove(mItems.at(index));
3105 delete mItems.at(index);
3106 mItems.removeAt(index);
3107 return true;
3108 } else
3109 return false;
3110 }
3111
3112 /*! \overload
3113
3114 Removes \a item from the legend.
3115
3116 Returns true, if successful.
3117
3118 \see clearItems
3119 */
3120 bool QCPLegend::removeItem(QCPAbstractLegendItem *item)
3121 {
3122 return removeItem(mItems.indexOf(item));
3123 }
3124
3125 /*!
3126 Removes all items from the legend.
3127 */
3128 void QCPLegend::clearItems()
3129 {
3130 qDeleteAll(mItems);
3131 mItems.clear();
3132 mItemBoundingBoxes.clear();
3133 }
3134
3135
3136 /*!
3137 Returns the legend items that are currently selected. If no items are selected,
3138 the list is empty.
3139
3140 \see QCPAbstractLegendItem::setSelected, setSelectable
3141 */
3142 QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const
3143 {
3144 QList<QCPAbstractLegendItem*> result;
3145 for (int i=0; i<mItems.size(); ++i)
3146 {
3147 if (mItems.at(i)->selected())
3148 result.append(mItems.at(i));
3149 }
3150 return result;
3151 }
3152
3153 /*!
3154 If \ref setAutoSize is true, the size needed to fit all legend contents is calculated and applied.
3155 Finally, the automatic positioning of the legend is performed, depending on the \ref
3156 setPositionStyle setting.
3157 */
3158 void QCPLegend::reArrange()
3159 {
3160 if (mAutoSize)
3161 {
3162 calculateAutoSize();
3163 }
3164 calculateAutoPosition();
3165 }
3166
3167 /*!
3168 Returns whether the point \a pos in pixels hits the legend rect.
3169
3170 \see selectTestItem
3171 */
3172 bool QCPLegend::selectTestLegend(const QPointF &pos) const
3173 {
3174 return QRect(mPosition, mSize).contains(pos.toPoint());
3175 }
3176
3177 /*!
3178 When the point \a pos in pixels hits a legend item, the item is returned. If no item is hit, 0 is
3179 returned.
3180
3181 \see selectTestLegend
3182 */
3183 QCPAbstractLegendItem *QCPLegend::selectTestItem(const QPoint pos) const
3184 {
3185 QMap<QCPAbstractLegendItem*, QRect>::const_iterator it;
3186 for (it = mItemBoundingBoxes.constBegin(); it != mItemBoundingBoxes.constEnd(); ++it)
3187 {
3188 if (it.value().contains(pos) && mItems.contains(it.key()))
3189 return it.key();
3190 }
3191 return 0;
3192 }
3193
3194 /*! \internal
3195
3196 Updates the spItems part of the selection state of this legend by going through all child items
3197 and checking their selected state.
3198
3199 If no items are selected and the current selected state contains spItems, it is removed and the
3200 \ref selectionChanged signal is emitted. If at least one item is selected and the current selection
3201 state does not contain spItems, it is added and the signal is emitted, too.
3202
3203 This function is called in the QCPAbstractLegendItem::setSelected functions to propagate their
3204 change to the parent legend.
3205 */
3206 void QCPLegend::updateSelectionState()
3207 {
3208 bool hasSelections = false;
3209 for (int i=0; i<mItems.size(); ++i)
3210 {
3211 if (mItems.at(i)->selected())
3212 {
3213 hasSelections = true;
3214 break;
3215 }
3216 }
3217
3218 // in the following we don't use setSelected because it would cause unnecessary
3219 // logic looping through items if spItems isn't set in the new state. (look at setSelected and you'll understand)
3220 if (hasSelections && !mSelected.testFlag(spItems))
3221 {
3222 mSelected |= spItems;
3223 emit selectionChanged(mSelected);
3224 } else if (!hasSelections && mSelected.testFlag(spItems))
3225 {
3226 mSelected &= ~spItems;
3227 emit selectionChanged(mSelected);
3228 }
3229 }
3230
3231 /*! \internal
3232
3233 Handles the selection \a event and returns true when the selection event hit any parts of the
3234 legend. If the selection state of any parts of the legend was changed, the output parameter \a
3235 modified is set to true.
3236
3237 When \a additiveSelecton is true, any new selections become selected in addition to the recent
3238 selections. The recent selections are not cleared. Further, clicking on one object multiple times
3239 in additive selection mode, toggles the selection of that object on and off.
3240
3241 To indicate that an event deselects the legend (i.e. the parts that are deselectable by the user,
3242 see \ref setSelectable), pass 0 as \a event.
3243 */
3244 bool QCPLegend::handleLegendSelection(QMouseEvent *event, bool additiveSelection, bool &modified)
3245 {
3246 modified = false;
3247 bool selectionFound = false;
3248
3249 if (event && selectTestLegend(event->pos())) // clicked inside legend somewhere
3250 {
3251 QCPAbstractLegendItem *ali = selectTestItem(event->pos());
3252 if (selectable().testFlag(QCPLegend::spItems) && ali && ali->selectable()) // items shall be selectable and item ali was clicked
3253 {
3254 selectionFound = true;
3255 // deselect legend box:
3256 if (!additiveSelection && selected().testFlag(QCPLegend::spLegendBox) && selectable().testFlag(QCPLegend::spLegendBox))
3257 setSelected(selected() & ~QCPLegend::spLegendBox);
3258 // first select clicked item:
3259 if (!ali->selected() || additiveSelection) // if additive selection, we toggle selection on and off per click
3260 {
3261 modified = true;
3262 ali->setSelected(!ali->selected());
3263 }
3264 // finally, deselect all other items (if we had deselected all first, the selectionChanged signal of QCPLegend might have been emitted twice):
3265 if (!additiveSelection)
3266 {
3267 for (int i=0; i<itemCount(); ++i)
3268 {
3269 if (item(i) != ali && item(i)->selected() && item(i)->selectable())
3270 {
3271 modified = true;
3272 item(i)->setSelected(false);
3273 }
3274 }
3275 }
3276 } else // no specific item clicked or items not selectable
3277 {
3278 // if items actually were selectable, this means none were clicked, deselect them:
3279 if (selectable().testFlag(QCPLegend::spItems) && selected().testFlag(QCPLegend::spItems) && !additiveSelection)
3280 {
3281 for (int i=0; i<itemCount(); ++i)
3282 {
3283 if (item(i)->selectable())
3284 item(i)->setSelected(false);
3285 }
3286 modified = true;
3287 }
3288 // if legend box is selectable, select it:
3289 if (selectable().testFlag(QCPLegend::spLegendBox))
3290 {
3291 if (!selected().testFlag(QCPLegend::spLegendBox) || additiveSelection)
3292 {
3293 selectionFound = true;
3294 setSelected(selected() ^ QCPLegend::spLegendBox); // xor because we always toggle
3295 modified = true;
3296 }
3297 }
3298 }
3299 } else if (selected() != QCPLegend::spNone && selectable() != QCPLegend::spNone && !additiveSelection) // legend not clicked, deselect it if selectable allows that (and all child items)
3300 {
3301 // only deselect parts that are allowed to be changed by user according to selectable()
3302 // deselect child items (and automatically removes spItems from selected state of legend, if last item gets deselected):
3303 if (selectable().testFlag(spItems))
3304 {
3305 for (int i=0; i<itemCount(); ++i)
3306 {
3307 if (item(i)->selected() && item(i)->selectable())
3308 {
3309 item(i)->setSelected(false);
3310 modified = true;
3311 }
3312 }
3313 }
3314 // only deselect parts that are allowed to be changed (are selectable). Don't forcibly remove
3315 // spItems, because some selected items might not be selectable, i.e. allowed to be deselected
3316 // by user interaction. If that's not the case, spItems will have been removed from selected()
3317 // state in previous loop by individual setSelected(false) calls on the items anyway.
3318 QCPLegend::SelectableParts newState = selected() & ~(selectable()&~spItems);
3319 if (newState != selected())
3320 {
3321 setSelected(newState);
3322 modified = true;
3323 }
3324 }
3325
3326 return selectionFound;
3327 }
3328
3329 /*! \internal
3330
3331 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
3332 before drawing main legend elements.
3333
3334 This is the antialiasing state the painter passed to the \ref draw method is in by default.
3335
3336 This function takes into account the local setting of the antialiasing flag as well as
3337 the overrides set e.g. with \ref QCustomPlot::setNotAntialiasedElements.
3338
3339 \see setAntialiased
3340 */
3341 void QCPLegend::applyDefaultAntialiasingHint(QCPPainter *painter) const
3342 {
3343 applyAntialiasingHint(painter, mAntialiased, QCP::aeLegend);
3344 }
3345
3346 /*! \internal
3347
3348 Returns the pen used to paint the border of the legend, taking into account the selection state
3349 of the legend box.
3350 */
3351 QPen QCPLegend::getBorderPen() const
3352 {
3353 return mSelected.testFlag(spLegendBox) ? mSelectedBorderPen : mBorderPen;
3354 }
3355
3356 /*! \internal
3357
3358 Returns the brush used to paint the background of the legend, taking into account the selection
3359 state of the legend box.
3360 */
3361 QBrush QCPLegend::getBrush() const
3362 {
3363 return mSelected.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
3364 }
3365
3366 /*! \internal
3367
3368 Draws the legend with the provided \a painter.
3369 */
3370 void QCPLegend::draw(QCPPainter *painter)
3371 {
3372 painter->setBrush(getBrush());
3373 painter->setPen(getBorderPen());
3374 // draw background rect:
3375 painter->drawRect(QRect(mPosition, mSize));
3376 // draw legend items:
3377 painter->setClipRect(QRect(mPosition, mSize).adjusted(1, 1, 0, 0));
3378 painter->setPen(QPen());
3379 painter->setBrush(Qt::NoBrush);
3380 int currentTop = mPosition.y()+mPaddingTop;
3381 for (int i=0; i<mItems.size(); ++i)
3382 {
3383 QSize itemSize = mItems.at(i)->size(QSize(mSize.width(), 0));
3384 QRect itemRect = QRect(QPoint(mPosition.x()+mPaddingLeft, currentTop), itemSize);
3385 mItemBoundingBoxes.insert(mItems.at(i), itemRect);
3386 painter->save();
3387 mItems.at(i)->applyAntialiasingHint(painter);
3388 mItems.at(i)->draw(painter, itemRect);
3389 painter->restore();
3390 currentTop += itemSize.height()+mItemSpacing;
3391 }
3392 }
3393
3394 /*! \internal
3395
3396 Goes through similar steps as \ref draw and calculates the width and height needed to
3397 fit all items and padding in the legend. The new calculated size is then applied to the mSize of
3398 this legend.
3399 */
3400 void QCPLegend::calculateAutoSize()
3401 {
3402 int width = mMinimumSize.width()-mPaddingLeft-mPaddingRight; // start with minimum width and only expand from there
3403 int currentTop;
3404 bool repeat = true;
3405 int repeatCount = 0;
3406 while (repeat && repeatCount < 3) // repeat until we find self-consistent width (usually 2 runs)
3407 {
3408 repeat = false;
3409 currentTop = mPaddingTop;
3410 for (int i=0; i<mItems.size(); ++i)
3411 {
3412 QSize s = mItems.at(i)->size(QSize(width, 0));
3413 currentTop += s.height();
3414 if (i < mItems.size()-1) // vertical spacer for all but last item
3415 currentTop += mItemSpacing;
3416 if (width < s.width())
3417 {
3418 width = s.width();
3419 repeat = true; // changed width, so need a new run with new width to let other items adapt their height to that new width
3420 }
3421 }
3422 repeatCount++;
3423 }
3424 if (repeat)
3425 qDebug() << Q_FUNC_INFO << "hit repeat limit for iterative width calculation";
3426 currentTop += mPaddingBottom;
3427 width += mPaddingLeft+mPaddingRight;
3428
3429 mSize.setWidth(width);
3430 if (currentTop > mMinimumSize.height())
3431 mSize.setHeight(currentTop);
3432 else
3433 mSize.setHeight(mMinimumSize.height());
3434 }
3435
3436 /*! \internal
3437
3438 Sets the position dependant on the \ref setPositionStyle setting and the margins.
3439 */
3440 void QCPLegend::calculateAutoPosition()
3441 {
3442 switch (mPositionStyle)
3443 {
3444 case psTopLeft:
3445 mPosition = mParentPlot->mAxisRect.topLeft() + QPoint(mMarginLeft, mMarginTop); break;
3446 case psTop:
3447 mPosition = mParentPlot->mAxisRect.topLeft() + QPoint(mParentPlot->mAxisRect.width()/2.0-mSize.width()/2.0, mMarginTop); break;
3448 case psTopRight:
3449 mPosition = mParentPlot->mAxisRect.topRight() + QPoint(-mMarginRight-mSize.width(), mMarginTop); break;
3450 case psRight:
3451 mPosition = mParentPlot->mAxisRect.topRight() + QPoint(-mMarginRight-mSize.width(), mParentPlot->mAxisRect.height()/2.0-mSize.height()/2.0); break;
3452 case psBottomRight:
3453 mPosition = mParentPlot->mAxisRect.bottomRight() + QPoint(-mMarginRight-mSize.width(), -mMarginBottom-mSize.height()); break;
3454 case psBottom:
3455 mPosition = mParentPlot->mAxisRect.bottomLeft() + QPoint(mParentPlot->mAxisRect.width()/2.0-mSize.width()/2.0, -mMarginBottom-mSize.height()); break;
3456 case psBottomLeft:
3457 mPosition = mParentPlot->mAxisRect.bottomLeft() + QPoint(mMarginLeft, -mMarginBottom-mSize.height()); break;
3458 case psLeft:
3459 mPosition = mParentPlot->mAxisRect.topLeft() + QPoint(mMarginLeft, mParentPlot->mAxisRect.height()/2.0-mSize.height()/2.0); break;
3460 case psManual: break;
3461 }
3462 }
3463
3464
3465 // ================================================================================
3466 // =================== QCPAxis
3467 // ================================================================================
3468
3469 /*! \class QCPAxis
3470 \brief Manages a single axis inside a QCustomPlot.
3471
3472 Usually doesn't need to be instantiated externally. Access %QCustomPlot's axes via
3473 QCustomPlot::xAxis (bottom), QCustomPlot::yAxis (left), QCustomPlot::xAxis2 (top) and
3474 QCustomPlot::yAxis2 (right).
3475 */
3476
3477 /* start of documentation of inline functions */
3478
3479 /*! \fn Qt::Orientation QCPAxis::orientation() const
3480
3481 Returns the orientation of the axis. The axis orientation (horizontal or vertical) is deduced
3482 from the axis type (left, top, right or bottom).
3483 */
3484
3485 /* end of documentation of inline functions */
3486 /* start of documentation of signals */
3487
3488 /*! \fn void QCPAxis::ticksRequest()
3489
3490 This signal is emitted when \ref setAutoTicks is false and the axis is about to generate tick
3491 labels and replot itself.
3492
3493 Modifying the tick positions can be done with \ref setTickVector. If you also want to control the
3494 tick labels, set \ref setAutoTickLabels to false and also provide the labels with \ref
3495 setTickVectorLabels.
3496
3497 If you only want static ticks you probably don't need this signal, since you can just set the
3498 tick vector (and possibly tick label vector) once. However, if you want to provide ticks (and
3499 maybe labels) dynamically, e.g. depending on the current axis range, connect a slot to this
3500 signal and set the vector/vectors there.
3501 */
3502
3503 /*! \fn void QCPAxis::rangeChanged(const QCPRange &newRange)
3504
3505 This signal is emitted when the range of this axis has changed. You can connect it to the \ref
3506 setRange slot of another axis to communicate the new range to the other axis, in order for it to
3507 be synchronized.
3508 */
3509
3510 /*! \fn void QCPAxis::selectionChanged(QCPAxis::SelectableParts selection)
3511
3512 This signal is emitted when the selection state of this axis has changed, either by user interaction
3513 or by a direct call to \ref setSelected.
3514 */
3515
3516 /* end of documentation of signals */
3517
3518 /*!
3519 Constructs an Axis instance of Type \a type inside \a parentPlot.
3520 */
3521 QCPAxis::QCPAxis(QCustomPlot *parentPlot, AxisType type) :
3522 QCPLayerable(parentPlot)
3523 {
3524 mLowestVisibleTick = 0;
3525 mHighestVisibleTick = -1;
3526 mGrid = new QCPGrid(this);
3527 setAxisType(type);
3528 setAxisRect(parentPlot->axisRect());
3529 setScaleType(stLinear);
3530 setScaleLogBase(10);
3531
3532 setAntialiased(false);
3533 setRange(0, 5);
3534 setRangeReversed(false);
3535
3536 setTicks(true);
3537 setTickStep(1);
3538 setAutoTickCount(6);
3539 setAutoTicks(true);
3540 setAutoTickLabels(true);
3541 setAutoTickStep(true);
3542 setTickLabelFont(parentPlot->font());
3543 setTickLabelColor(Qt::black);
3544 setTickLength(5);
3545 setTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap));
3546 setTickLabels(true);
3547 setTickLabelType(ltNumber);
3548 setTickLabelRotation(0);
3549 setDateTimeFormat("hh:mm:ss\ndd.MM.yy");
3550 setNumberFormat("gbd");
3551 setNumberPrecision(6);
3552 setLabel("");
3553 setLabelFont(parentPlot->font());
3554 setLabelColor(Qt::black);
3555
3556 setAutoSubTicks(true);
3557 setSubTickCount(4);
3558 setSubTickLength(2);
3559 setSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap));
3560 setBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap));
3561
3562 setSelected(spNone);
3563 setSelectable(spAxis | spTickLabels | spAxisLabel);
3564 QFont selTickLabelFont = tickLabelFont();
3565 selTickLabelFont.setBold(true);
3566 setSelectedTickLabelFont(selTickLabelFont);
3567 QFont selLabelFont = labelFont();
3568 selLabelFont.setBold(true);
3569 setSelectedLabelFont(selLabelFont);
3570 setSelectedTickLabelColor(Qt::blue);
3571 setSelectedLabelColor(Qt::blue);
3572 QPen blueThickPen(Qt::blue, 2);
3573 setSelectedBasePen(blueThickPen);
3574 setSelectedTickPen(blueThickPen);
3575 setSelectedSubTickPen(blueThickPen);
3576
3577 setPadding(0);
3578 if (type == atTop)
3579 {
3580 setTickLabelPadding(3);
3581 setLabelPadding(6);
3582 } else if (type == atRight)
3583 {
3584 setTickLabelPadding(7);
3585 setLabelPadding(12);
3586 } else if (type == atBottom)
3587 {
3588 setTickLabelPadding(3);
3589 setLabelPadding(3);
3590 } else if (type == atLeft)
3591 {
3592 setTickLabelPadding(5);
3593 setLabelPadding(10);
3594 }
3595 }
3596
3597 QCPAxis::~QCPAxis()
3598 {
3599 delete mGrid;
3600 }
3601
3602 /* No documentation as it is a property getter */
3603 QString QCPAxis::numberFormat() const
3604 {
3605 QString result;
3606 result.append(mNumberFormatChar);
3607 if (mNumberBeautifulPowers)
3608 {
3609 result.append("b");
3610 if (mNumberMultiplyCross)
3611 result.append("c");
3612 }
3613 return result;
3614 }
3615
3616 /*! \internal
3617
3618 Sets the axis type. This determines the \ref orientation and together with the current axis rect
3619 (see \ref setAxisRect), the position of the axis. Depending on \a type, ticks, tick labels, and
3620 label are drawn on corresponding sides of the axis base line.
3621 */
3622 void QCPAxis::setAxisType(AxisType type)
3623 {
3624 mAxisType = type;
3625 mOrientation = (type == atBottom || type == atTop) ? Qt::Horizontal : Qt::Vertical;
3626 }
3627
3628 /*! \internal
3629
3630 Sets the axis rect. The axis uses this rect to position itself within the plot,
3631 together with the information of its type (\ref setAxisType). Theoretically it's possible to give
3632 a plot's axes different axis rects (e.g. for gaps between them), however, they are currently all
3633 synchronized by the QCustomPlot::setAxisRect function.
3634 */
3635 void QCPAxis::setAxisRect(const QRect &rect)
3636 {
3637 mAxisRect = rect;
3638 }
3639
3640 /*!
3641 Sets whether the axis uses a linear scale or a logarithmic scale. If \a type is set to \ref
3642 stLogarithmic, the logarithm base can be set with \ref setScaleLogBase. In logarithmic axis
3643 scaling, major tick marks appear at all powers of the logarithm base. Properties like tick step
3644 (\ref setTickStep) don't apply in logarithmic scaling. If you wish a decimal base but less major
3645 ticks, consider choosing a logarithm base of 100, 1000 or even higher.
3646
3647 If \a type is \ref stLogarithmic and the number format (\ref setNumberFormat) uses the 'b' option
3648 (beautifully typeset decimal powers), the display usually is "1 [multiplication sign] 10
3649 [superscript] n", which looks unnatural for logarithmic scaling (the "1 [multiplication sign]"
3650 part). To only display the decimal power, set the number precision to zero with
3651 \ref setNumberPrecision.
3652 */
3653 void QCPAxis::setScaleType(ScaleType type)
3654 {
3655 mScaleType = type;
3656 if (mScaleType == stLogarithmic)
3657 mRange = mRange.sanitizedForLogScale();
3658 }
3659
3660 /*!
3661 If \ref setScaleType is set to \ref stLogarithmic, \a base will be the logarithm base of the
3662 scaling. In logarithmic axis scaling, major tick marks appear at all powers of \a base.
3663
3664 Properties like tick step (\ref setTickStep) don't apply in logarithmic scaling. If you wish a decimal base but
3665 less major ticks, consider choosing \a base 100, 1000 or even higher.
3666 */
3667 void QCPAxis::setScaleLogBase(double base)
3668 {
3669 if (base > 1)
3670 {
3671 mScaleLogBase = base;
3672 mScaleLogBaseLogInv = 1.0/qLn(mScaleLogBase); // buffer for faster baseLog() calculation
3673 } else
3674 qDebug() << Q_FUNC_INFO << "Invalid logarithmic scale base (must be greater 1):" << base;
3675 }
3676
3677 /*!
3678 Sets the range of the axis.
3679
3680 This slot may be connected with the \ref rangeChanged signal of another axis so this axis
3681 is always synchronized with the other axis range, when it changes.
3682
3683 To invert the direction of an axis range, use \ref setRangeReversed.
3684 */
3685 void QCPAxis::setRange(const QCPRange &range)
3686 {
3687 if (range.lower == mRange.lower && range.upper == mRange.upper)
3688 return;
3689
3690 if (!QCPRange::validRange(range)) return;
3691 if (mScaleType == stLogarithmic)
3692 {
3693 mRange = range.sanitizedForLogScale();
3694 } else
3695 {
3696 mRange = range.sanitizedForLinScale();
3697 }
3698 emit rangeChanged(mRange);
3699 }
3700
3701 /*!
3702 Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface.
3703 (When \ref QCustomPlot::setInteractions contains iSelectAxes.)
3704
3705 However, even when \a selectable is set to a value not allowing the selection of a specific part,
3706 it is still possible to set the selection of this part manually, by calling \ref setSelected
3707 directly.
3708
3709 \see SelectablePart, setSelected
3710 */
3711 void QCPAxis::setSelectable(const SelectableParts &selectable)
3712 {
3713 mSelectable = selectable;
3714 }
3715
3716 /*!
3717 Sets the selected state of the respective axis parts described by \ref SelectablePart. When a part
3718 is selected, it uses a different pen/font.
3719
3720 The entire selection mechanism for axes is handled automatically when \ref
3721 QCustomPlot::setInteractions contains iSelectAxes. You only need to call this function when you
3722 wish to change the selection state manually.
3723
3724 This function can change the selection state of a part even when \ref setSelectable was set to a
3725 value that actually excludes the part.
3726
3727 emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
3728
3729 \see SelectablePart, setSelectable, selectTest, setSelectedBasePen, setSelectedTickPen, setSelectedSubTickPen,
3730 setSelectedTickLabelFont, setSelectedLabelFont, setSelectedTickLabelColor, setSelectedLabelColor
3731 */
3732 void QCPAxis::setSelected(const SelectableParts &selected)
3733 {
3734 if (mSelected != selected)
3735 {
3736 mSelected = selected;
3737 emit selectionChanged(mSelected);
3738 }
3739 }
3740
3741 /*!
3742 \overload
3743 Sets the lower and upper bound of the axis range.
3744
3745 To invert the direction of an axis range, use \ref setRangeReversed.
3746
3747 There is also a slot to set a range, see \ref setRange(const QCPRange &range).
3748 */
3749 void QCPAxis::setRange(double lower, double upper)
3750 {
3751 if (lower == mRange.lower && upper == mRange.upper)
3752 return;
3753
3754 if (!QCPRange::validRange(lower, upper)) return;
3755 mRange.lower = lower;
3756 mRange.upper = upper;
3757 if (mScaleType == stLogarithmic)
3758 {
3759 mRange = mRange.sanitizedForLogScale();
3760 } else
3761 {
3762 mRange = mRange.sanitizedForLinScale();
3763 }
3764 emit rangeChanged(mRange);
3765 }
3766
3767 /*!
3768 \overload
3769 Sets the range of the axis.
3770
3771 \param position the \a position coordinate indicates together with the \a alignment parameter, where
3772 the new range will be positioned.
3773 \param size defines the size (upper-lower) of the new axis range.
3774 \param alignment determines how \a position is to be interpreted.\n
3775 If \a alignment is Qt::AlignLeft, \a position will be the lower bound of the range.\n
3776 If \a alignment is Qt::AlignRight, \a position will be the upper bound of the range.\n
3777 If \a alignment is Qt::AlignCenter, the new range will be centered around \a position.\n
3778 Any other values for \a alignment will default to Qt::AlignCenter.
3779 */
3780 void QCPAxis::setRange(double position, double size, Qt::AlignmentFlag alignment)
3781 {
3782 if (alignment == Qt::AlignLeft)
3783 setRange(position, position+size);
3784 else if (alignment == Qt::AlignRight)
3785 setRange(position-size, position);
3786 else // alignment == Qt::AlignCenter
3787 setRange(position-size/2.0, position+size/2.0);
3788 }
3789
3790 /*!
3791 Sets the lower bound of the axis range, independently of the upper bound.
3792 \see setRange
3793 */
3794 void QCPAxis::setRangeLower(double lower)
3795 {
3796 if (mRange.lower == lower)
3797 return;
3798
3799 mRange.lower = lower;
3800 if (mScaleType == stLogarithmic)
3801 {
3802 mRange = mRange.sanitizedForLogScale();
3803 } else
3804 {
3805 mRange = mRange.sanitizedForLinScale();
3806 }
3807 emit rangeChanged(mRange);
3808 }
3809
3810 /*!
3811 Sets the upper bound of the axis range, independently of the lower bound.
3812 \see setRange
3813 */
3814 void QCPAxis::setRangeUpper(double upper)
3815 {
3816 if (mRange.upper == upper)
3817 return;
3818
3819 mRange.upper = upper;
3820 if (mScaleType == stLogarithmic)
3821 {
3822 mRange = mRange.sanitizedForLogScale();
3823 } else
3824 {
3825 mRange = mRange.sanitizedForLinScale();
3826 }
3827 emit rangeChanged(mRange);
3828 }
3829
3830 /*!
3831 Sets whether the axis range (direction) is displayed reversed. Normally, the values on horizontal
3832 axes increase left to right, on vertical axes bottom to top. When \a reversed is set to true, the
3833 direction of increasing values is inverted. Note that the range and data interface stays the same
3834 for reversed axes, e.g. the \a lower part of the \ref setRange interface will still reference the
3835 mathematically smaller number than the \a upper part.
3836 */
3837 void QCPAxis::setRangeReversed(bool reversed)
3838 {
3839 mRangeReversed = reversed;
3840 }
3841
3842 /*!
3843 Sets whether the grid of this axis is drawn antialiased or not.
3844
3845 Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
3846 QCustomPlot::setNotAntialiasedElements.
3847 */
3848 void QCPAxis::setAntialiasedGrid(bool enabled)
3849 {
3850 mGrid->setAntialiased(enabled);
3851 }
3852
3853 /*!
3854 Sets whether the sub grid of this axis is drawn antialiased or not.
3855
3856 Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
3857 QCustomPlot::setNotAntialiasedElements.
3858 */
3859 void QCPAxis::setAntialiasedSubGrid(bool enabled)
3860 {
3861 mGrid->setAntialiasedSubGrid(enabled);
3862 }
3863
3864 /*!
3865 Sets whether the zero line of this axis is drawn antialiased or not.
3866
3867 Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
3868 QCustomPlot::setNotAntialiasedElements.
3869 */
3870 void QCPAxis::setAntialiasedZeroLine(bool enabled)
3871 {
3872 mGrid->setAntialiasedZeroLine(enabled);
3873 }
3874
3875 /*!
3876 Sets whether the grid lines are visible.
3877 \see setSubGrid, setGridPen, setZeroLinePen
3878 */
3879 void QCPAxis::setGrid(bool show)
3880 {
3881 mGrid->setVisible(show);
3882 }
3883
3884 /*!
3885 Sets whether the sub grid lines are visible.
3886 \see setGrid, setSubGridPen, setZeroLinePen
3887 */
3888 void QCPAxis::setSubGrid(bool show)
3889 {
3890 mGrid->setSubGridVisible(show);
3891 }
3892
3893 /*!
3894 Sets whether the tick positions should be calculated automatically (either from an automatically
3895 generated tick step or a tick step provided manually via \ref setTickStep, see \ref setAutoTickStep).
3896
3897 If \a on is set to false, you must provide the tick positions manually via \ref setTickVector.
3898 For these manual ticks you may let QCPAxis generate the appropriate labels automatically
3899 by setting/leaving \ref setAutoTickLabels true. If you also wish to control the displayed labels
3900 manually, set \ref setAutoTickLabels to false and provide the label strings with \ref setTickVectorLabels.
3901
3902 If you need dynamically calculated tick vectors (and possibly tick label vectors), set the
3903 vectors in a slot connected to the \ref ticksRequest signal.
3904 */
3905 void QCPAxis::setAutoTicks(bool on)
3906 {
3907 mAutoTicks = on;
3908 }
3909
3910 /*!
3911 When \ref setAutoTickStep is true, \a approximateCount determines how many ticks should be generated
3912 in the visible range approximately.
3913 */
3914 void QCPAxis::setAutoTickCount(int approximateCount)
3915 {
3916 mAutoTickCount = approximateCount;
3917 }
3918
3919 /*!
3920 Sets whether the tick labels are generated automatically depending on the tick label type
3921 (\ref ltNumber or \ref ltDateTime).
3922
3923 If \a on is set to false, you should provide the tick labels via \ref setTickVectorLabels. This
3924 is usually used in a combination with \ref setAutoTicks set to false for complete control over
3925 tick positions and labels, e.g. when the ticks should be at multiples of pi and show "2pi", "3pi"
3926 etc. as tick labels.
3927
3928 If you need dynamically calculated tick vectors (and possibly tick label vectors), set the
3929 vectors in a slot connected to the \ref ticksRequest signal.
3930 */
3931 void QCPAxis::setAutoTickLabels(bool on)
3932 {
3933 mAutoTickLabels = on;
3934 }
3935
3936 /*!
3937 Sets whether the tick step, i.e. the interval between two (major) ticks, is calculated
3938 automatically. If \a on is set to true, the axis finds a tick step that is reasonable for human
3939 readable plots. This means the tick step mantissa is chosen such that it's either a multiple of
3940 two or ends in 0.5. The number of ticks the algorithm aims for within the visible range can be
3941 set with \ref setAutoTickCount. It's not guaranteed that this number of ticks is met exactly, but
3942 approximately within a tolerance of two or three.
3943
3944 If \a on is set to false, you may set the tick step manually with \ref setTickStep.
3945 */
3946 void QCPAxis::setAutoTickStep(bool on)
3947 {
3948 mAutoTickStep = on;
3949 }
3950
3951 /*!
3952 Sets whether the number of sub ticks in one tick interval is determined automatically.
3953 This works, as long as the tick step mantissa is a multiple of 0.5 (which it is, when
3954 \ref setAutoTickStep is enabled).\n
3955 When \a on is set to false, you may set the sub tick count with \ref setSubTickCount manually.
3956 */
3957 void QCPAxis::setAutoSubTicks(bool on)
3958 {
3959 mAutoSubTicks = on;
3960 }
3961
3962 /*!
3963 Sets whether tick marks are displayed. Setting \a show to false does not imply, that tick labels
3964 are invisible, too. To achieve that, see \ref setTickLabels.
3965 */
3966 void QCPAxis::setTicks(bool show)
3967 {
3968 mTicks = show;
3969 }
3970
3971 /*!
3972 Sets whether tick labels are displayed.
3973 */
3974 void QCPAxis::setTickLabels(bool show)
3975 {
3976 mTickLabels = show;
3977 }
3978
3979 /*!
3980 Sets the distance between the axis base line (or any tick marks pointing outward) and the tick labels.
3981 \see setLabelPadding, setPadding
3982 */
3983 void QCPAxis::setTickLabelPadding(int padding)
3984 {
3985 mTickLabelPadding = padding;
3986 }
3987
3988 /*!
3989 Sets whether the tick labels display numbers or dates/times.\n
3990 If \a type is set to \ref ltNumber, the format specifications of \ref setNumberFormat apply.\n
3991 If \a type is set to \ref ltDateTime, the format specifications of \ref setDateTimeFormat apply.\n
3992 In QCustomPlot, date/time coordinates are double numbers representing the seconds since 1970-01-01T00:00:00 UTC.
3993 This format can be retrieved from QDateTime objects with the QDateTime::toTime_t() function. Since this
3994 only gives a resolution of one second, there is also the QDateTime::toMSecsSinceEpoch() function which
3995 returns the timespan described above in milliseconds. Divide its return value by 1000.0 to get a value with
3996 the format needed for date/time plotting, this time with a resolution of one millisecond.
3997 */
3998 void QCPAxis::setTickLabelType(LabelType type)
3999 {
4000 mTickLabelType = type;
4001 }
4002
4003 /*!
4004 Sets the font of the tick labels, i.e. the numbers drawn next to tick marks.
4005
4006 \see setTickLabelColor
4007 */
4008 void QCPAxis::setTickLabelFont(const QFont &font)
4009 {
4010 mTickLabelFont = font;
4011 }
4012
4013 /*!
4014 Sets the color of the tick labels, i.e. the numbers drawn next to tick marks.
4015
4016 \see setTickLabelFont
4017 */
4018 void QCPAxis::setTickLabelColor(const QColor &color)
4019 {
4020 mTickLabelColor = color;
4021 }
4022
4023 /*!
4024 Sets the rotation of the tick labels, i.e. the numbers drawn next to tick marks. If \a degrees
4025 is zero, the labels are drawn normally. Else, the tick labels are drawn rotated by \a degrees
4026 clockwise. The specified angle is bound to values from -90 to 90 degrees.
4027 */
4028 void QCPAxis::setTickLabelRotation(double degrees)
4029 {
4030 mTickLabelRotation = qBound(-90.0, degrees, 90.0);
4031 }
4032
4033 /*!
4034 Sets the format in which dates and times are displayed as tick labels, if \ref setTickLabelType is \ref ltDateTime.
4035 for details about the \a format string, see the documentation of QDateTime::toString().
4036 Newlines can be inserted with "\n".
4037 */
4038 void QCPAxis::setDateTimeFormat(const QString &format)
4039 {
4040 mDateTimeFormat = format;
4041 }
4042
4043 /*!
4044 Sets the number format for the numbers drawn as tick labels (if tick label type is \ref
4045 ltNumber). This \a formatCode is an extended version of the format code used e.g. by
4046 QString::number() and QLocale::toString(). For reference about that, see the "Argument Formats"
4047 section in the detailed description of the QString class. \a formatCode is a string of one, two
4048 or three characters. The first character is identical to the normal format code used by Qt. In
4049 short, this means: 'e'/'E' scientific format, 'f' fixed format, 'g'/'G' scientific or fixed,
4050 whichever is shorter.
4051
4052 The second and third characters are optional and specific to QCustomPlot:\n
4053 If the first char was 'e' or 'g', numbers are/might be displayed in the scientific format, e.g.
4054 "5.5e9", which is ugly in a plot. So when the second char of \a formatCode is set to 'b' (for
4055 "beautiful"), those exponential numbers are formatted in a more natural way, i.e. "5.5
4056 [multiplication sign] 10 [superscript] 9". By default, the multiplication sign is a centered dot.
4057 If instead a cross should be shown (as is usual in the USA), the third char of \a formatCode can
4058 be set to 'c'. The inserted multiplication signs are the UTF-8 characters 215 (0xD7) for the
4059 cross and 183 (0xB7) for the dot.
4060
4061 If the scale type (\ref setScaleType) is \ref stLogarithmic and the \a formatCode uses the 'b'
4062 option (beautifully typeset decimal powers), the display usually is "1 [multiplication sign] 10
4063 [superscript] n", which looks unnatural for logarithmic scaling (the "1 [multiplication sign]"
4064 part). To only display the decimal power, set the number precision to zero with \ref
4065 setNumberPrecision.
4066
4067 Examples for \a formatCode:
4068 \li \c g normal format code behaviour. If number is small, fixed format is used, if number is large,
4069 normal scientific format is used
4070 \li \c gb If number is small, fixed format is used, if number is large, scientific format is used with
4071 beautifully typeset decimal powers and a dot as multiplication sign
4072 \li \c ebc All numbers are in scientific format with beautifully typeset decimal power and a cross as
4073 multiplication sign
4074 \li \c fb illegal format code, since fixed format doesn't support (or need) beautifully typeset decimal
4075 powers. Format code will be reduced to 'f'.
4076 \li \c hello illegal format code, since first char is not 'e', 'E', 'f', 'g' or 'G'. Current format
4077 code will not be changed.
4078 */
4079 void QCPAxis::setNumberFormat(const QString &formatCode)
4080 {
4081 if (formatCode.length() < 1) return;
4082
4083 // interpret first char as number format char:
4084 QString allowedFormatChars = "eEfgG";
4085 if (allowedFormatChars.contains(formatCode.at(0)))
4086 {
4087 mNumberFormatChar = formatCode.at(0).toAscii();
4088 } else
4089 {
4090 qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode;
4091 return;
4092 }
4093 if (formatCode.length() < 2)
4094 {
4095 mNumberBeautifulPowers = false;
4096 mNumberMultiplyCross = false;
4097 return;
4098 }
4099
4100 // interpret second char as indicator for beautiful decimal powers:
4101 if (formatCode.at(1) == 'b' && (mNumberFormatChar == 'e' || mNumberFormatChar == 'g'))
4102 {
4103 mNumberBeautifulPowers = true;
4104 } else
4105 {
4106 qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode;
4107 return;
4108 }
4109 if (formatCode.length() < 3)
4110 {
4111 mNumberMultiplyCross = false;
4112 return;
4113 }
4114
4115 // interpret third char as indicator for dot or cross multiplication symbol:
4116 if (formatCode.at(2) == 'c')
4117 {
4118 mNumberMultiplyCross = true;
4119 } else if (formatCode.at(2) == 'd')
4120 {
4121 mNumberMultiplyCross = false;
4122 } else
4123 {
4124 qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
4125 return;
4126 }
4127 }
4128
4129 /*!
4130 Sets the precision of the numbers drawn as tick labels. See QLocale::toString(double i, char f,
4131 int prec) for details. The effect of precisions are most notably for number Formats starting with
4132 'e', see \ref setNumberFormat
4133
4134 If the scale type (\ref setScaleType) is \ref stLogarithmic and the number format (\ref
4135 setNumberFormat) uses the 'b' format code (beautifully typeset decimal powers), the display
4136 usually is "1 [multiplication sign] 10 [superscript] n", which looks unnatural for logarithmic
4137 scaling (the "1 [multiplication sign]" part). To only display the decimal power, set \a precision
4138 to zero.
4139 */
4140 void QCPAxis::setNumberPrecision(int precision)
4141 {
4142 mNumberPrecision = precision;
4143 }
4144
4145 /*!
4146 If \ref setAutoTickStep is set to false, use this function to set the tick step manually.
4147 The tick step is the interval between (major) ticks, in plot coordinates.
4148 \see setSubTickCount
4149 */
4150 void QCPAxis::setTickStep(double step)
4151 {
4152 mTickStep = step;
4153 }
4154
4155 /*!
4156 If you want full control over what ticks (and possibly labels) the axes show, this function is
4157 used to set the coordinates at which ticks will appear.\ref setAutoTicks must be disabled, else
4158 the provided tick vector will be overwritten with automatically generated tick coordinates. The
4159 labels of the ticks can either be generated automatically when \ref setAutoTickLabels is left
4160 enabled, or be set manually with \ref setTickVectorLabels, when \ref setAutoTickLabels is
4161 disabled.
4162
4163 \a vec is a vector containing the positions of the ticks.
4164
4165 \see setTickVectorLabels
4166 */
4167 void QCPAxis::setTickVector(const QVector<double> &vec)
4168 {
4169 mTickVector = vec;
4170 }
4171
4172 /*!
4173 If you want full control over what ticks and labels the axes show, this function is used to set a
4174 number of QStrings that will be displayed at the tick positions which you need to provide with
4175 \ref setTickVector. These two vectors should have the same size. (Note that you need to disable
4176 \ref setAutoTicks and \ref setAutoTickLabels first.)
4177
4178 \a vec is a vector containing the labels of the ticks.
4179
4180 \see setTickVector
4181 */
4182 void QCPAxis::setTickVectorLabels(const QVector<QString> &vec)
4183 {
4184 mTickVectorLabels = vec;
4185 }
4186
4187 /*!
4188 Sets the length of the ticks in pixels. \a inside is the length the ticks will reach inside the
4189 plot and \a outside is the length they will reach outside the plot. If \a outside is greater than
4190 zero, the tick labels will increase their distance to the axis accordingly, so they won't collide
4191 with the ticks.
4192 \see setSubTickLength
4193 */
4194 void QCPAxis::setTickLength(int inside, int outside)
4195 {
4196 mTickLengthIn = inside;
4197 mTickLengthOut = outside;
4198 }
4199
4200 /*!
4201 Sets the number of sub ticks in one (major) tick step. A sub tick count of three for example,
4202 divides the tick intervals in four sub intervals.
4203
4204 By default, the number of sub ticks is chosen automatically in a reasonable manner as long as
4205 the mantissa of the tick step is a multiple of 0.5 (which it is, when \ref setAutoTickStep is enabled).
4206 If you want to disable automatic sub ticks and use this function to set the count manually, see
4207 \ref setAutoSubTicks.
4208 */
4209 void QCPAxis::setSubTickCount(int count)
4210 {
4211 mSubTickCount = count;
4212 }
4213
4214 /*!
4215 Sets the length of the subticks in pixels. \a inside is the length the subticks will reach inside the
4216 plot and \a outside is the length they will reach outside the plot. If \a outside is greater than
4217 zero, the tick labels will increase their distance to the axis accordingly, so they won't collide
4218 with the ticks.
4219 \see setTickLength
4220 */
4221 void QCPAxis::setSubTickLength(int inside, int outside)
4222 {
4223 mSubTickLengthIn = inside;
4224 mSubTickLengthOut = outside;
4225 }
4226
4227 /*!
4228 Sets the pen, the axis base line is drawn with.
4229
4230 \see setTickPen, setSubTickPen
4231 */
4232 void QCPAxis::setBasePen(const QPen &pen)
4233 {
4234 mBasePen = pen;
4235 }
4236
4237 /*!
4238 Sets the pen, grid lines are drawn with.
4239 \see setSubGridPen, setZeroLinePen
4240 */
4241 void QCPAxis::setGridPen(const QPen &pen)
4242 {
4243 mGrid->setPen(pen);
4244 }
4245
4246 /*!
4247 Sets the pen, the sub grid lines are drawn with.
4248 (By default, subgrid drawing needs to be enabled first with \ref setSubGrid.)
4249 \see setGridPen, setZeroLinePen
4250 */
4251 void QCPAxis::setSubGridPen(const QPen &pen)
4252 {
4253 mGrid->setSubGridPen(pen);
4254 }
4255
4256 /*!
4257 Sets the pen with which a single grid-like line will be drawn at value position zero. The line
4258 will be drawn instead of a grid line at that position, and not on top. To disable the drawing of
4259 a zero-line, set \a pen to Qt::NoPen. Then, if \ref setGrid is enabled, a grid line will be
4260 drawn instead.
4261 \see setGrid, setGridPen
4262 */
4263 void QCPAxis::setZeroLinePen(const QPen &pen)
4264 {
4265 mGrid->setZeroLinePen(pen);
4266 }
4267
4268 /*!
4269 Sets the pen, tick marks will be drawn with.
4270 \see setTickLength, setBasePen
4271 */
4272 void QCPAxis::setTickPen(const QPen &pen)
4273 {
4274 mTickPen = pen;
4275 }
4276
4277 /*!
4278 Sets the pen, subtick marks will be drawn with.
4279 \see setSubTickCount, setSubTickLength, setBasePen
4280 */
4281 void QCPAxis::setSubTickPen(const QPen &pen)
4282 {
4283 mSubTickPen = pen;
4284 }
4285
4286 /*!
4287 Sets the font of the axis label.
4288
4289 \see setLabelColor
4290 */
4291 void QCPAxis::setLabelFont(const QFont &font)
4292 {
4293 mLabelFont = font;
4294 }
4295
4296 /*!
4297 Sets the color of the axis label.
4298
4299 \see setLabelFont
4300 */
4301 void QCPAxis::setLabelColor(const QColor &color)
4302 {
4303 mLabelColor = color;
4304 }
4305
4306 /*!
4307 Sets the axis label that will be shown below/above or next to the axis, depending on its orientation.
4308 */
4309 void QCPAxis::setLabel(const QString &str)
4310 {
4311 mLabel = str;
4312 }
4313
4314 /*!
4315 Sets the distance between the tick labels and the axis label.
4316 \see setTickLabelPadding, setPadding
4317 */
4318 void QCPAxis::setLabelPadding(int padding)
4319 {
4320 mLabelPadding = padding;
4321 }
4322
4323 /*!
4324 Sets the padding of the axis.
4325
4326 When \ref QCustomPlot::setAutoMargin is enabled, the padding is the additional distance to the
4327 respective widget border, that is left blank. If \a padding is zero (default), the auto margin
4328 mechanism will find a margin that the axis label (or tick label, if no axis label is set) barely
4329 fits inside the QCustomPlot widget. To give the label closest to the border some freedom,
4330 increase \a padding.
4331
4332 The axis padding has no meaning if \ref QCustomPlot::setAutoMargin is disabled.
4333
4334 \see setLabelPadding, setTickLabelPadding
4335 */
4336 void QCPAxis::setPadding(int padding)
4337 {
4338 mPadding = padding;
4339 }
4340
4341 /*!
4342 Sets the font that is used for tick labels when they are selected.
4343
4344 \see setTickLabelFont, setSelectable, setSelected, QCustomPlot::setInteractions
4345 */
4346 void QCPAxis::setSelectedTickLabelFont(const QFont &font)
4347 {
4348 mSelectedTickLabelFont = font;
4349 }
4350
4351 /*!
4352 Sets the font that is used for the axis label when it is selected.
4353
4354 \see setLabelFont, setSelectable, setSelected, QCustomPlot::setInteractions
4355 */
4356 void QCPAxis::setSelectedLabelFont(const QFont &font)
4357 {
4358 mSelectedLabelFont = font;
4359 }
4360
4361 /*!
4362 Sets the color that is used for tick labels when they are selected.
4363
4364 \see setTickLabelColor, setSelectable, setSelected, QCustomPlot::setInteractions
4365 */
4366 void QCPAxis::setSelectedTickLabelColor(const QColor &color)
4367 {
4368 mSelectedTickLabelColor = color;
4369 }
4370
4371 /*!
4372 Sets the color that is used for the axis label when it is selected.
4373
4374 \see setLabelColor, setSelectable, setSelected, QCustomPlot::setInteractions
4375 */
4376 void QCPAxis::setSelectedLabelColor(const QColor &color)
4377 {
4378 mSelectedLabelColor = color;
4379 }
4380
4381 /*!
4382 Sets the pen that is used to draw the axis base line when selected.
4383
4384 \see setBasePen, setSelectable, setSelected, QCustomPlot::setInteractions
4385 */
4386 void QCPAxis::setSelectedBasePen(const QPen &pen)
4387 {
4388 mSelectedBasePen = pen;
4389 }
4390
4391 /*!
4392 Sets the pen that is used to draw the (major) ticks when selected.
4393
4394 \see setTickPen, setSelectable, setSelected, QCustomPlot::setInteractions
4395 */
4396 void QCPAxis::setSelectedTickPen(const QPen &pen)
4397 {
4398 mSelectedTickPen = pen;
4399 }
4400
4401 /*!
4402 Sets the pen that is used to draw the subticks when selected.
4403
4404 \see setSubTickPen, setSelectable, setSelected, QCustomPlot::setInteractions
4405 */
4406 void QCPAxis::setSelectedSubTickPen(const QPen &pen)
4407 {
4408 mSelectedSubTickPen = pen;
4409 }
4410
4411 /*!
4412 If the scale type (\ref setScaleType) is \ref stLinear, \a diff is added to the lower and upper
4413 bounds of the range. The range is simply moved by \a diff.
4414
4415 If the scale type is \ref stLogarithmic, the range bounds are multiplied by \a diff. This
4416 corresponds to an apparent "linear" move in logarithmic scaling by a distance of log(diff).
4417 */
4418 void QCPAxis::moveRange(double diff)
4419 {
4420 if (mScaleType == stLinear)
4421 {
4422 mRange.lower += diff;
4423 mRange.upper += diff;
4424 } else // mScaleType == stLogarithmic
4425 {
4426 mRange.lower *= diff;
4427 mRange.upper *= diff;
4428 }
4429 emit rangeChanged(mRange);
4430 }
4431
4432 /*!
4433 Scales the range of this axis by \a factor around the coordinate \a center. For example, if \a
4434 factor is 2.0, \a center is 1.0, then the axis range will double its size, and the point at
4435 coordinate 1.0 won't have changed its position in the QCustomPlot widget (i.e. coordinates
4436 around 1.0 will have moved symmetrically closer to 1.0).
4437 */
4438 void QCPAxis::scaleRange(double factor, double center)
4439 {
4440
4441 if (mScaleType == stLinear)
4442 {
4443 QCPRange newRange;
4444 newRange.lower = (mRange.lower-center)*factor + center;
4445 newRange.upper = (mRange.upper-center)*factor + center;
4446 if (QCPRange::validRange(newRange))
4447 mRange = newRange.sanitizedForLinScale();
4448 } else // mScaleType == stLogarithmic
4449 {
4450 if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range
4451 {
4452 QCPRange newRange;
4453 newRange.lower = pow(mRange.lower/center, factor)*center;
4454 newRange.upper = pow(mRange.upper/center, factor)*center;
4455 if (QCPRange::validRange(newRange))
4456 mRange = newRange.sanitizedForLogScale();
4457 } else
4458 qDebug() << Q_FUNC_INFO << "center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center;
4459 }
4460 emit rangeChanged(mRange);
4461 }
4462
4463 /*!
4464 Sets the range of this axis to have a certain scale \a ratio to \a otherAxis. For example, if \a
4465 ratio is 1, this axis is the \a yAxis and \a otherAxis is \a xAxis, graphs plotted with those
4466 axes will appear in a 1:1 ratio, independent of the aspect ratio the axis rect has. This is an
4467 operation that changes the range of this axis once, it doesn't fix the scale ratio indefinitely.
4468 Consequently calling this function in the constructor won't have the desired effect, since the
4469 widget's dimensions aren't defined yet, and a resizeEvent will follow.
4470 */
4471 void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio)
4472 {
4473 int otherPixelSize, ownPixelSize;
4474
4475 if (otherAxis->orientation() == Qt::Horizontal)
4476 otherPixelSize = otherAxis->mAxisRect.width();
4477 else
4478 otherPixelSize = otherAxis->mAxisRect.height();
4479
4480 if (orientation() == Qt::Horizontal)
4481 ownPixelSize = mAxisRect.width();
4482 else
4483 ownPixelSize = mAxisRect.height();
4484
4485 double newRangeSize = ratio*otherAxis->mRange.size()*ownPixelSize/(double)otherPixelSize;
4486 setRange(range().center(), newRangeSize, Qt::AlignCenter);
4487 }
4488
4489 /*!
4490 Transforms \a value (in pixel coordinates of the QCustomPlot widget) to axis coordinates.
4491 */
4492 double QCPAxis::pixelToCoord(double value) const
4493 {
4494 if (orientation() == Qt::Horizontal)
4495 {
4496 if (mScaleType == stLinear)
4497 {
4498 if (!mRangeReversed)
4499 return (value-mAxisRect.left())/(double)mAxisRect.width()*mRange.size()+mRange.lower;
4500 else
4501 return -(value-mAxisRect.left())/(double)mAxisRect.width()*mRange.size()+mRange.upper;
4502 } else // mScaleType == stLogarithmic
4503 {
4504 if (!mRangeReversed)
4505 return pow(mRange.upper/mRange.lower, (value-mAxisRect.left())/(double)mAxisRect.width())*mRange.lower;
4506 else
4507 return pow(mRange.upper/mRange.lower, (mAxisRect.left()-value)/(double)mAxisRect.width())*mRange.upper;
4508 }
4509 } else // orientation() == Qt::Vertical
4510 {
4511 if (mScaleType == stLinear)
4512 {
4513 if (!mRangeReversed)
4514 return (mAxisRect.bottom()-value)/(double)mAxisRect.height()*mRange.size()+mRange.lower;
4515 else
4516 return -(mAxisRect.bottom()-value)/(double)mAxisRect.height()*mRange.size()+mRange.upper;
4517 } else // mScaleType == stLogarithmic
4518 {
4519 if (!mRangeReversed)
4520 return pow(mRange.upper/mRange.lower, (mAxisRect.bottom()-value)/(double)mAxisRect.height())*mRange.lower;
4521 else
4522 return pow(mRange.upper/mRange.lower, (value-mAxisRect.bottom())/(double)mAxisRect.height())*mRange.upper;
4523 }
4524 }
4525 }
4526
4527 /*!
4528 Transforms \a value (in coordinates of the axis) to pixel coordinates of the QCustomPlot widget.
4529 */
4530 double QCPAxis::coordToPixel(double value) const
4531 {
4532 if (orientation() == Qt::Horizontal)
4533 {
4534 if (mScaleType == stLinear)
4535 {
4536 if (!mRangeReversed)
4537 return (value-mRange.lower)/mRange.size()*mAxisRect.width()+mAxisRect.left();
4538 else
4539 return (mRange.upper-value)/mRange.size()*mAxisRect.width()+mAxisRect.left();
4540 } else // mScaleType == stLogarithmic
4541 {
4542 if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
4543 return !mRangeReversed ? mAxisRect.right()+200 : mAxisRect.left()-200;
4544 else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
4545 return !mRangeReversed ? mAxisRect.left()-200 : mAxisRect.right()+200;
4546 else
4547 {
4548 if (!mRangeReversed)
4549 return baseLog(value/mRange.lower)/baseLog(mRange.upper/mRange.lower)*mAxisRect.width()+mAxisRect.left();
4550 else
4551 return baseLog(mRange.upper/value)/baseLog(mRange.upper/mRange.lower)*mAxisRect.width()+mAxisRect.left();
4552 }
4553 }
4554 } else // orientation() == Qt::Vertical
4555 {
4556 if (mScaleType == stLinear)
4557 {
4558 if (!mRangeReversed)
4559 return mAxisRect.bottom()-(value-mRange.lower)/mRange.size()*mAxisRect.height();
4560 else
4561 return mAxisRect.bottom()-(mRange.upper-value)/mRange.size()*mAxisRect.height();
4562 } else // mScaleType == stLogarithmic
4563 {
4564 if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
4565 return !mRangeReversed ? mAxisRect.top()-200 : mAxisRect.bottom()+200;
4566 else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
4567 return !mRangeReversed ? mAxisRect.bottom()+200 : mAxisRect.top()-200;
4568 else
4569 {
4570 if (!mRangeReversed)
4571 return mAxisRect.bottom()-baseLog(value/mRange.lower)/baseLog(mRange.upper/mRange.lower)*mAxisRect.height();
4572 else
4573 return mAxisRect.bottom()-baseLog(mRange.upper/value)/baseLog(mRange.upper/mRange.lower)*mAxisRect.height();
4574 }
4575 }
4576 }
4577 }
4578
4579 /*!
4580 Returns the part of the axis that is hit by \a pos (in pixels). The return value of this function
4581 is independent of the user-selectable parts defined with \ref setSelectable. Further, this
4582 function does not change the current selection state of the axis.
4583
4584 If the axis is not visible (\ref setVisible), this function always returns \ref spNone.
4585
4586 \see setSelected, setSelectable, QCustomPlot::setInteractions
4587 */
4588 QCPAxis::SelectablePart QCPAxis::selectTest(const QPointF &pos) const
4589 {
4590 if (!mVisible)
4591 return spNone;
4592
4593 if (mAxisSelectionBox.contains(pos.toPoint()))
4594 return spAxis;
4595 else if (mTickLabelsSelectionBox.contains(pos.toPoint()))
4596 return spTickLabels;
4597 else if (mLabelSelectionBox.contains(pos.toPoint()))
4598 return spAxisLabel;
4599 else
4600 return spNone;
4601 }
4602
4603 /*! \internal
4604
4605 This function is called before the grid and axis is drawn, in order to prepare the tick vector,
4606 sub tick vector and tick label vector. If \ref setAutoTicks is set to true, appropriate tick
4607 values are determined automatically via \ref generateAutoTicks. If it's set to false, the signal
4608 ticksRequest is emitted, which can be used to provide external tick positions. Then the sub tick
4609 vectors and tick label vectors are created.
4610 */
4611 void QCPAxis::setupTickVectors()
4612 {
4613 if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return;
4614
4615 // fill tick vectors, either by auto generating or by notifying user to fill the vectors himself
4616 if (mAutoTicks)
4617 {
4618 generateAutoTicks();
4619 } else
4620 {
4621 emit ticksRequest();
4622 }
4623
4624 visibleTickBounds(mLowestVisibleTick, mHighestVisibleTick);
4625 if (mTickVector.isEmpty())
4626 {
4627 mSubTickVector.clear();
4628 return;
4629 }
4630
4631 // generate subticks between ticks:
4632 mSubTickVector.resize((mTickVector.size()-1)*mSubTickCount);
4633 if (mSubTickCount > 0)
4634 {
4635 double subTickStep = 0;
4636 double subTickPosition = 0;
4637 int subTickIndex = 0;
4638 bool done = false;
4639 for (int i=1; i<mTickVector.size(); ++i)
4640 {
4641 subTickStep = (mTickVector.at(i)-mTickVector.at(i-1))/(double)(mSubTickCount+1);
4642 for (int k=1; k<=mSubTickCount; ++k)
4643 {
4644 subTickPosition = mTickVector.at(i-1) + k*subTickStep;
4645 if (subTickPosition < mRange.lower)
4646 continue;
4647 if (subTickPosition > mRange.upper)
4648 {
4649 done = true;
4650 break;
4651 }
4652 mSubTickVector[subTickIndex] = subTickPosition;
4653 subTickIndex++;
4654 }
4655 if (done) break;
4656 }
4657 mSubTickVector.resize(subTickIndex);
4658 }
4659
4660 // generate tick labels according to tick positions:
4661 mExponentialChar = mParentPlot->locale().exponential(); // will be needed when drawing the numbers generated here, in drawTickLabel()
4662 mPositiveSignChar = mParentPlot->locale().positiveSign(); // will be needed when drawing the numbers generated here, in drawTickLabel()
4663 if (mAutoTickLabels)
4664 {
4665 int vecsize = mTickVector.size();
4666 mTickVectorLabels.resize(vecsize);
4667 if (mTickLabelType == ltNumber)
4668 {
4669 for (int i=0; i<vecsize; ++i)
4670 mTickVectorLabels[i] = mParentPlot->locale().toString(mTickVector.at(i), mNumberFormatChar, mNumberPrecision);
4671 } else if (mTickLabelType == ltDateTime)
4672 {
4673 for (int i=0; i<vecsize; ++i)
4674 {
4675 #if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) // use fromMSecsSinceEpoch function if available, to gain sub-second accuracy on tick labels (e.g. for format "hh:mm:ss:zzz")
4676 mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromTime_t(mTickVector.at(i)), mDateTimeFormat);
4677 #else
4678 mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromMSecsSinceEpoch(mTickVector.at(i)*1000), mDateTimeFormat);
4679 #endif
4680 }
4681 }
4682 } else // mAutoTickLabels == false
4683 {
4684 if (mAutoTicks) // ticks generated automatically, but not ticklabels, so emit ticksRequest here for labels
4685 {
4686 emit ticksRequest();
4687 }
4688 // make sure provided tick label vector has correct (minimal) length:
4689 if (mTickVectorLabels.size() < mTickVector.size())
4690 mTickVectorLabels.resize(mTickVector.size());
4691 }
4692 }
4693
4694 /*! \internal
4695
4696 If \ref setAutoTicks is set to true, this function is called by \ref setupTickVectors to
4697 generate reasonable tick positions (and subtick count). The algorithm tries to create
4698 approximately <tt>mAutoTickCount</tt> ticks (set via \ref setAutoTickCount), taking into account,
4699 that tick mantissas that are divisable by two or end in .5 are nice to look at and practical in
4700 linear scales. If the scale is logarithmic, one tick is generated at every power of the current
4701 logarithm base, set via \ref setScaleLogBase.
4702 */
4703 void QCPAxis::generateAutoTicks()
4704 {
4705 if (mScaleType == stLinear)
4706 {
4707 if (mAutoTickStep)
4708 {
4709 // Generate tick positions according to linear scaling:
4710 mTickStep = mRange.size()/(double)(mAutoTickCount+1e-10); // mAutoTickCount ticks on average, the small addition is to prevent jitter on exact integers
4711 double magnitudeFactor = qPow(10.0, qFloor(qLn(mTickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
4712 double tickStepMantissa = mTickStep/magnitudeFactor;
4713 if (tickStepMantissa < 5)
4714 {
4715 // round digit after decimal point to 0.5
4716 mTickStep = (int)(tickStepMantissa*2)/2.0*magnitudeFactor;
4717 } else
4718 {
4719 // round to first digit in multiples of 2
4720 mTickStep = (int)((tickStepMantissa/10.0)*5)/5.0*10*magnitudeFactor;
4721 }
4722 }
4723 if (mAutoSubTicks)
4724 mSubTickCount = calculateAutoSubTickCount(mTickStep);
4725 // Generate tick positions according to mTickStep:
4726 int firstStep = floor(mRange.lower/mTickStep);
4727 int lastStep = ceil(mRange.upper/mTickStep);
4728 int tickcount = lastStep-firstStep+1;
4729 if (tickcount < 0) tickcount = 0;
4730 mTickVector.resize(tickcount);
4731 for (int i=0; i<tickcount; ++i)
4732 {
4733 mTickVector[i] = (firstStep+i)*mTickStep;
4734 }
4735 } else // mScaleType == stLogarithmic
4736 {
4737 // Generate tick positions according to logbase scaling:
4738 if (mRange.lower > 0 && mRange.upper > 0) // positive range
4739 {
4740 double lowerMag = basePow((int)floor(baseLog(mRange.lower)));
4741 double currentMag = lowerMag;
4742 mTickVector.clear();
4743 mTickVector.append(currentMag);
4744 while (currentMag < mRange.upper && currentMag > 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
4745 {
4746 currentMag *= mScaleLogBase;
4747 mTickVector.append(currentMag);
4748 }
4749 } else if (mRange.lower < 0 && mRange.upper < 0) // negative range
4750 {
4751 double lowerMag = -basePow((int)ceil(baseLog(-mRange.lower)));
4752 double currentMag = lowerMag;
4753 mTickVector.clear();
4754 mTickVector.append(currentMag);
4755 while (currentMag < mRange.upper && currentMag < 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
4756 {
4757 currentMag /= mScaleLogBase;
4758 mTickVector.append(currentMag);
4759 }
4760 } else // invalid range for logarithmic scale, because lower and upper have different sign
4761 {
4762 mTickVector.clear();
4763 qDebug() << Q_FUNC_INFO << "Invalid range for logarithmic plot: " << mRange.lower << "-" << mRange.upper;
4764 }
4765 }
4766 }
4767
4768 /*! \internal
4769
4770 Called by generateAutoTicks when \ref setAutoSubTicks is set to true. Depending on the \a
4771 tickStep between two major ticks on the axis, a different number of sub ticks is appropriate. For
4772 Example taking 4 sub ticks for a \a tickStep of 1 makes more sense than taking 5 sub ticks,
4773 because this corresponds to a sub tick step of 0.2, instead of the less intuitive 0.16666. Note
4774 that a subtick count of 4 means dividing the major tick step into 5 sections.
4775
4776 This is implemented by a hand made lookup for integer tick steps as well as fractional tick steps
4777 with a fractional part of (approximately) 0.5. If a tick step is different (i.e. has no
4778 fractional part close to 0.5), the currently set sub tick count (\ref setSubTickCount) is
4779 returned.
4780 */
4781 int QCPAxis::calculateAutoSubTickCount(double tickStep) const
4782 {
4783 int result = mSubTickCount; // default to current setting, if no proper value can be found
4784
4785 // get mantissa of tickstep:
4786 double magnitudeFactor = qPow(10.0, qFloor(qLn(tickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
4787 double tickStepMantissa = tickStep/magnitudeFactor;
4788
4789 // separate integer and fractional part of mantissa:
4790 double epsilon = 0.01;
4791 double intPartf;
4792 int intPart;
4793 double fracPart = modf(tickStepMantissa, &intPartf);
4794 intPart = intPartf;
4795
4796 // handle cases with (almost) integer mantissa:
4797 if (fracPart < epsilon || 1.0-fracPart < epsilon)
4798 {
4799 if (1.0-fracPart < epsilon)
4800 intPart++;
4801 switch (intPart)
4802 {
4803 case 1: result = 4; break; // 1.0 -> 0.2 substep
4804 case 2: result = 3; break; // 2.0 -> 0.5 substep
4805 case 3: result = 2; break; // 3.0 -> 1.0 substep
4806 case 4: result = 3; break; // 4.0 -> 1.0 substep
4807 case 5: result = 4; break; // 5.0 -> 1.0 substep
4808 case 6: result = 2; break; // 6.0 -> 2.0 substep
4809 case 7: result = 6; break; // 7.0 -> 1.0 substep
4810 case 8: result = 3; break; // 8.0 -> 2.0 substep
4811 case 9: result = 2; break; // 9.0 -> 3.0 substep
4812 }
4813 } else
4814 {
4815 // handle cases with significantly fractional mantissa:
4816 if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa
4817 {
4818 switch (intPart)
4819 {
4820 case 1: result = 2; break; // 1.5 -> 0.5 substep
4821 case 2: result = 4; break; // 2.5 -> 0.5 substep
4822 case 3: result = 4; break; // 3.5 -> 0.7 substep
4823 case 4: result = 2; break; // 4.5 -> 1.5 substep
4824 case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with autoTickStep from here on)
4825 case 6: result = 4; break; // 6.5 -> 1.3 substep
4826 case 7: result = 2; break; // 7.5 -> 2.5 substep
4827 case 8: result = 4; break; // 8.5 -> 1.7 substep
4828 case 9: result = 4; break; // 9.5 -> 1.9 substep
4829 }
4830 }
4831 // if mantissa fraction isnt 0.0 or 0.5, don't bother finding good sub tick marks, leave default
4832 }
4833
4834 return result;
4835 }
4836
4837 /*! \internal
4838
4839 The main draw function of an axis, called by QCustomPlot::draw for each axis. Draws axis
4840 baseline, major ticks, subticks, tick labels and axis label.
4841
4842 The selection boxes (mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox) are set
4843 here, too.
4844 */
4845 void QCPAxis::draw(QCPPainter *painter)
4846 {
4847 QPoint origin;
4848 if (mAxisType == atLeft)
4849 origin = mAxisRect.bottomLeft();
4850 else if (mAxisType == atRight)
4851 origin = mAxisRect.bottomRight();
4852 else if (mAxisType == atTop)
4853 origin = mAxisRect.topLeft();
4854 else if (mAxisType == atBottom)
4855 origin = mAxisRect.bottomLeft();
4856
4857 double xCor = 0, yCor = 0; // paint system correction, for pixel exact matches (affects baselines and ticks of top/right axes)
4858 switch (mAxisType)
4859 {
4860 case atTop: yCor = -1; break;
4861 case atRight: xCor = 1; break;
4862 default: break;
4863 }
4864
4865 int margin = 0;
4866 int lowTick = mLowestVisibleTick;
4867 int highTick = mHighestVisibleTick;
4868 double t; // helper variable, result of coordinate-to-pixel transforms
4869
4870 // draw baseline:
4871 painter->setPen(getBasePen());
4872 if (orientation() == Qt::Horizontal)
4873 painter->drawLine(QLineF(origin+QPointF(xCor, yCor), origin+QPointF(mAxisRect.width()+xCor, yCor)));
4874 else
4875 painter->drawLine(QLineF(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -mAxisRect.height()+yCor)));
4876
4877 // draw ticks:
4878 if (mTicks)
4879 {
4880 painter->setPen(getTickPen());
4881 // direction of ticks ("inward" is right for left axis and left for right axis)
4882 int tickDir = (mAxisType == atBottom || mAxisType == atRight) ? -1 : 1;
4883 if (orientation() == Qt::Horizontal)
4884 {
4885 for (int i=lowTick; i <= highTick; ++i)
4886 {
4887 t = coordToPixel(mTickVector.at(i)); // x
4888 painter->drawLine(QLineF(t+xCor, origin.y()-mTickLengthOut*tickDir+yCor, t+xCor, origin.y()+mTickLengthIn*tickDir+yCor));
4889 }
4890 } else
4891 {
4892 for (int i=lowTick; i <= highTick; ++i)
4893 {
4894 t = coordToPixel(mTickVector.at(i)); // y
4895 painter->drawLine(QLineF(origin.x()-mTickLengthOut*tickDir+xCor, t+yCor, origin.x()+mTickLengthIn*tickDir+xCor, t+yCor));
4896 }
4897 }
4898 }
4899
4900 // draw subticks:
4901 if (mTicks && mSubTickCount > 0)
4902 {
4903 painter->setPen(getSubTickPen());
4904 // direction of ticks ("inward" is right for left axis and left for right axis)
4905 int tickDir = (mAxisType == atBottom || mAxisType == atRight) ? -1 : 1;
4906 if (orientation() == Qt::Horizontal)
4907 {
4908 for (int i=0; i<mSubTickVector.size(); ++i) // no need to check bounds because subticks are always only created inside current mRange
4909 {
4910 t = coordToPixel(mSubTickVector.at(i));
4911 painter->drawLine(QLineF(t+xCor, origin.y()-mSubTickLengthOut*tickDir+yCor, t+xCor, origin.y()+mSubTickLengthIn*tickDir+yCor));
4912 }
4913 } else
4914 {
4915 for (int i=0; i<mSubTickVector.size(); ++i)
4916 {
4917 t = coordToPixel(mSubTickVector.at(i));
4918 painter->drawLine(QLineF(origin.x()-mSubTickLengthOut*tickDir+xCor, t+yCor, origin.x()+mSubTickLengthIn*tickDir+xCor, t+yCor));
4919 }
4920 }
4921 }
4922 margin += qMax(0, qMax(mTickLengthOut, mSubTickLengthOut));
4923
4924 // tick labels:
4925 QSize tickLabelsSize(0, 0); // size of largest tick label, for offset calculation of axis label
4926 if (mTickLabels)
4927 {
4928 margin += mTickLabelPadding;
4929 painter->setFont(getTickLabelFont());
4930 painter->setPen(QPen(getTickLabelColor()));
4931 for (int i=lowTick; i <= highTick; ++i)
4932 {
4933 t = coordToPixel(mTickVector.at(i));
4934 drawTickLabel(painter, t, margin, mTickVectorLabels.at(i), &tickLabelsSize);
4935 }
4936 }
4937 if (orientation() == Qt::Horizontal)
4938 margin += tickLabelsSize.height();
4939 else
4940 margin += tickLabelsSize.width();
4941
4942 // axis label:
4943 QRect labelBounds;
4944 if (!mLabel.isEmpty())
4945 {
4946 margin += mLabelPadding;
4947 painter->setFont(getLabelFont());
4948 painter->setPen(QPen(getLabelColor()));
4949 labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, mLabel);
4950 if (mAxisType == atLeft)
4951 {
4952 QTransform oldTransform = painter->transform();
4953 painter->translate((origin.x()-margin-labelBounds.height()), origin.y());
4954 painter->rotate(-90);
4955 painter->drawText(0, 0, mAxisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, mLabel);
4956 painter->setTransform(oldTransform);
4957 }
4958 else if (mAxisType == atRight)
4959 {
4960 QTransform oldTransform = painter->transform();
4961 painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-mAxisRect.height());
4962 painter->rotate(90);
4963 painter->drawText(0, 0, mAxisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, mLabel);
4964 painter->setTransform(oldTransform);
4965 }
4966 else if (mAxisType == atTop)
4967 painter->drawText(origin.x(), origin.y()-margin-labelBounds.height(), mAxisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, mLabel);
4968 else if (mAxisType == atBottom)
4969 painter->drawText(origin.x(), origin.y()+margin, mAxisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, mLabel);
4970 }
4971
4972 // set selection boxes:
4973 int selAxisOutSize = qMax(qMax(mTickLengthOut, mSubTickLengthOut), mParentPlot->selectionTolerance());
4974 int selAxisInSize = mParentPlot->selectionTolerance();
4975 int selTickLabelSize = (orientation()==Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
4976 int selTickLabelOffset = qMax(mTickLengthOut, mSubTickLengthOut)+mTickLabelPadding;
4977 int selLabelSize = labelBounds.height();
4978 int selLabelOffset = selTickLabelOffset+selTickLabelSize+mLabelPadding;
4979 if (mAxisType == atLeft)
4980 {
4981 mAxisSelectionBox.setCoords(mAxisRect.left()-selAxisOutSize, mAxisRect.top(), mAxisRect.left()+selAxisInSize, mAxisRect.bottom());
4982 mTickLabelsSelectionBox.setCoords(mAxisRect.left()-selTickLabelOffset-selTickLabelSize, mAxisRect.top(), mAxisRect.left()-selTickLabelOffset, mAxisRect.bottom());
4983 mLabelSelectionBox.setCoords(mAxisRect.left()-selLabelOffset-selLabelSize, mAxisRect.top(), mAxisRect.left()-selLabelOffset, mAxisRect.bottom());
4984 } else if (mAxisType == atRight)
4985 {
4986 mAxisSelectionBox.setCoords(mAxisRect.right()-selAxisInSize, mAxisRect.top(), mAxisRect.right()+selAxisOutSize, mAxisRect.bottom());
4987 mTickLabelsSelectionBox.setCoords(mAxisRect.right()+selTickLabelOffset+selTickLabelSize, mAxisRect.top(), mAxisRect.right()+selTickLabelOffset, mAxisRect.bottom());
4988 mLabelSelectionBox.setCoords(mAxisRect.right()+selLabelOffset+selLabelSize, mAxisRect.top(), mAxisRect.right()+selLabelOffset, mAxisRect.bottom());
4989 } else if (mAxisType == atTop)
4990 {
4991 mAxisSelectionBox.setCoords(mAxisRect.left(), mAxisRect.top()-selAxisOutSize, mAxisRect.right(), mAxisRect.top()+selAxisInSize);
4992 mTickLabelsSelectionBox.setCoords(mAxisRect.left(), mAxisRect.top()-selTickLabelOffset-selTickLabelSize, mAxisRect.right(), mAxisRect.top()-selTickLabelOffset);
4993 mLabelSelectionBox.setCoords(mAxisRect.left(), mAxisRect.top()-selLabelOffset-selLabelSize, mAxisRect.right(), mAxisRect.top()-selLabelOffset);
4994 } else if (mAxisType == atBottom)
4995 {
4996 mAxisSelectionBox.setCoords(mAxisRect.left(), mAxisRect.bottom()-selAxisInSize, mAxisRect.right(), mAxisRect.bottom()+selAxisOutSize);
4997 mTickLabelsSelectionBox.setCoords(mAxisRect.left(), mAxisRect.bottom()+selTickLabelOffset+selTickLabelSize, mAxisRect.right(), mAxisRect.bottom()+selTickLabelOffset);
4998 mLabelSelectionBox.setCoords(mAxisRect.left(), mAxisRect.bottom()+selLabelOffset+selLabelSize, mAxisRect.right(), mAxisRect.bottom()+selLabelOffset);
4999 }
5000 // draw hitboxes for debug purposes:
5001 //painter->drawRects(QVector<QRect>() << mAxisSelectionBox << mTickLabelsSelectionBox << mLabelSelectionBox);
5002 }
5003
5004 /*! \internal
5005
5006 Draws a single tick label with the provided \a painter. The tick label is always bound to an axis
5007 in one direction (distance to axis in that direction is however controllable via \a
5008 distanceToAxis in pixels). The position in the other direction is passed in the \a position
5009 parameter. Hence for the bottom axis, \a position would indicate the horizontal pixel position
5010 (not coordinate!), at which the label should be drawn.
5011
5012 In order to draw the axis label after all the tick labels in a position, that doesn't overlap
5013 with the tick labels, we need to know the largest tick label size. This is done by passing a \a
5014 tickLabelsSize to all \ref drawTickLabel calls during the process of drawing all tick labels of
5015 one axis. \a tickLabelSize is only expanded, if the drawn label exceeds the value \a
5016 tickLabelsSize currently holds.
5017
5018 This function is also responsible for turning ugly exponential numbers "5.5e9" into a more
5019 beautifully typeset format "5.5 [multiplication sign] 10 [superscript] 9". This feature is
5020 controlled with \ref setNumberFormat.
5021
5022 The label is drawn with the font and pen that are currently set on the \a painter. To draw
5023 superscripted powers, the font is temporarily made smaller by a fixed factor.
5024 */
5025 void QCPAxis::drawTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
5026 {
5027 // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly!
5028
5029 // determine whether beautiful decimal powers should be used
5030 bool useBeautifulPowers = false;
5031 int ePos = -1;
5032 if (mAutoTickLabels && mNumberBeautifulPowers && mTickLabelType == ltNumber)
5033 {
5034 ePos = text.indexOf('e');
5035 if (ePos > -1)
5036 useBeautifulPowers = true;
5037 }
5038
5039 // calculate text bounding rects and do string preparation for beautiful decimal powers:
5040 QRect bounds, baseBounds, expBounds;
5041 QString basePart, expPart;
5042 QFont bugFixFont(painter->font());
5043 bugFixFont.setPointSizeF(bugFixFont.pointSizeF()+0.05); // QFontMetrics.boundingRect has a bug for exact point sizes that make the results oscillate due to internal rounding
5044 QFont expFont;
5045 if (useBeautifulPowers)
5046 {
5047 // split string parts for part of number/symbol that will be drawn normally and part that will be drawn as exponent:
5048 basePart = text.left(ePos);
5049 // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base:
5050 if (mScaleType == stLogarithmic && basePart == "1")
5051 basePart = "10";
5052 else
5053 basePart += (mNumberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + "10";
5054 expPart = text.mid(ePos+1);
5055 // clip "+" and leading zeros off expPart:
5056 while (expPart.at(1) == '0' && expPart.length() > 2) // length > 2 so we leave one zero when numberFormatChar is 'e'
5057 expPart.remove(1, 1);
5058 if (expPart.at(0) == mPositiveSignChar)
5059 expPart.remove(0, 1);
5060 // prepare smaller font for exponent:
5061 expFont = painter->font();
5062 expFont.setPointSize(expFont.pointSize()*0.75);
5063 // calculate bounding rects of base part, exponent part and total one:
5064 QFontMetrics fontMetrics(bugFixFont);
5065 baseBounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, basePart);
5066 QFontMetrics expFontMetrics(expFont);
5067 expBounds = expFontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, expPart);
5068 bounds = baseBounds.adjusted(0, 0, expBounds.width(), 0);
5069 } else // useBeautifulPowers == false
5070 {
5071 QFontMetrics fontMetrics(bugFixFont);
5072 bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, text);
5073 }
5074
5075 // if using rotated tick labels, transform bounding rect, too:
5076 QRect rotatedBounds = bounds;
5077 if (!qFuzzyIsNull(mTickLabelRotation))
5078 {
5079 QTransform transform;
5080 transform.rotate(mTickLabelRotation);
5081 rotatedBounds = transform.mapRect(bounds);
5082 }
5083 // expand passed tickLabelsSize if current tick label is larger:
5084 if (rotatedBounds.width() > tickLabelsSize->width())
5085 tickLabelsSize->setWidth(rotatedBounds.width());
5086 if (rotatedBounds.height() > tickLabelsSize->height())
5087 tickLabelsSize->setHeight(rotatedBounds.height());
5088
5089 /*
5090 calculate coordinates (non-trivial, for best visual appearance): short explanation for bottom
5091 axis: The anchor, i.e. the point in the label that is placed horizontally under the
5092 corresponding tick is always on the label side that is closer to the axis (e.g. the left side
5093 of the text when we're rotating clockwise). On that side, the height is halved and the
5094 resulting point is defined the anchor. This way, a 90 degree rotated text will be centered
5095 under the tick (i.e. displaced horizontally by half its height). At the same time, a 45 degree
5096 rotated text will "point toward" its tick, as is typical for rotated tick labels.
5097 */
5098 bool doRotation = !qFuzzyIsNull(mTickLabelRotation);
5099 double radians = mTickLabelRotation/180.0*M_PI;
5100 int x=0,y=0;
5101 if (mAxisType == atLeft)
5102 {
5103 if (doRotation)
5104 {
5105 if (mTickLabelRotation > 0)
5106 {
5107 x = mAxisRect.left()-qCos(radians)*bounds.width()-distanceToAxis;
5108 y = position-qSin(radians)*bounds.width()-qCos(radians)*bounds.height()/2.0;
5109 } else
5110 {
5111 x = mAxisRect.left()-qCos(-radians)*bounds.width()-qSin(-radians)*bounds.height()-distanceToAxis;
5112 y = position+qSin(-radians)*bounds.width()-qCos(-radians)*bounds.height()/2.0;
5113 }
5114 } else
5115 {
5116 x = mAxisRect.left()-bounds.width()-distanceToAxis;
5117 y = position-bounds.height()/2.0;
5118 }
5119 } else if (mAxisType == atRight)
5120 {
5121 if (doRotation)
5122 {
5123 if (mTickLabelRotation > 0)
5124 {
5125 x = mAxisRect.right()+qSin(radians)*bounds.height()+distanceToAxis;
5126 y = position-qCos(radians)*bounds.height()/2.0;
5127 } else
5128 {
5129 x = mAxisRect.right()+distanceToAxis;
5130 y = position-qCos(-radians)*bounds.height()/2.0;
5131 }
5132 } else
5133 {
5134 x = mAxisRect.right()+distanceToAxis;
5135 y = position-bounds.height()/2.0;
5136 }
5137 } else if (mAxisType == atTop)
5138 {
5139 if (doRotation)
5140 {
5141 if (mTickLabelRotation > 0)
5142 {
5143 x = position-qCos(radians)*bounds.width()+qSin(radians)*bounds.height()/2.0;
5144 y = mAxisRect.top()-qSin(radians)*bounds.width()-qCos(radians)*bounds.height()-distanceToAxis;
5145 } else
5146 {
5147 x = position-qSin(-radians)*bounds.height()/2.0;
5148 y = mAxisRect.top()-qCos(-radians)*bounds.height()-distanceToAxis;
5149 }
5150 } else
5151 {
5152 x = position-bounds.width()/2.0;
5153 y = mAxisRect.top()-bounds.height()-distanceToAxis;
5154 }
5155 } else if (mAxisType == atBottom)
5156 {
5157 if (doRotation)
5158 {
5159 if (mTickLabelRotation > 0)
5160 {
5161 x = position+qSin(radians)*bounds.height()/2.0;
5162 y = mAxisRect.bottom()+distanceToAxis;
5163 } else
5164 {
5165 x = position-qCos(-radians)*bounds.width()-qSin(-radians)*bounds.height()/2.0;
5166 y = mAxisRect.bottom()+qSin(-radians)*bounds.width()+distanceToAxis;
5167 }
5168 } else
5169 {
5170 x = position-bounds.width()/2.0;
5171 y = mAxisRect.bottom()+distanceToAxis;
5172 }
5173 }
5174
5175 // if label would be partly clipped by widget border on sides, don't draw it:
5176 if (orientation() == Qt::Horizontal)
5177 {
5178 if (x+bounds.width() > mParentPlot->mViewport.right() ||
5179 x < mParentPlot->mViewport.left())
5180 return;
5181 } else
5182 {
5183 if (y+bounds.height() > mParentPlot->mViewport.bottom() ||
5184 y < mParentPlot->mViewport.top())
5185 return;
5186 }
5187
5188 // transform painter to position/rotation:
5189 QTransform oldTransform = painter->transform();
5190 painter->translate(x, y);
5191 if (doRotation)
5192 painter->rotate(mTickLabelRotation);
5193 // draw text:
5194 if (useBeautifulPowers)
5195 {
5196 // draw base:
5197 painter->drawText(0, 0, 0, 0, Qt::TextDontClip, basePart);
5198 // draw exponent:
5199 QFont normalFont = painter->font();
5200 painter->setFont(expFont);
5201 painter->drawText(baseBounds.width()+1, 0, expBounds.width(), expBounds.height(), Qt::TextDontClip, expPart);
5202 painter->setFont(normalFont);
5203 } else // useBeautifulPowers == false
5204 {
5205 painter->drawText(0, 0, bounds.width(), bounds.height(), Qt::TextDontClip | Qt::AlignHCenter, text);
5206 }
5207
5208 // reset rotation/translation transform to what it was before:
5209 painter->setTransform(oldTransform);
5210 }
5211
5212 /*! \internal
5213
5214 Simulates the steps done by \ref drawTickLabel by calculating bounding boxes of the text label to
5215 be drawn, depending on number format etc. Since we only want the largest tick label for the
5216 margin calculation, the passed \a tickLabelsSize isn't overridden with the calculated label size,
5217 but it's only expanded, if it's currently set to a smaller width/height.
5218 */
5219 void QCPAxis::getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
5220 {
5221 // This function does the same as drawTickLabel but omits the actual drawing
5222 // changes involve creating extra QFontMetrics instances for font, since painter->fontMetrics() isn't available
5223
5224 // determine whether beautiful powers should be used
5225 bool useBeautifulPowers = false;
5226 int ePos=-1;
5227 if (mAutoTickLabels && mNumberBeautifulPowers && mTickLabelType == ltNumber)
5228 {
5229 ePos = text.indexOf(mExponentialChar);
5230 if (ePos > -1)
5231 useBeautifulPowers = true;
5232 }
5233
5234 // calculate and draw text, depending on whether beautiful powers are applicable or not:
5235 QRect bounds, baseBounds, expBounds;
5236 QString basePart, expPart;
5237 QFont bugFixFont(font);
5238 bugFixFont.setPointSizeF(bugFixFont.pointSizeF()+0.05); // QFontMetrics.boundingRect has a bug for exact point sizes that make the results oscillate due to internal rounding
5239 QFont expFont;
5240 if (useBeautifulPowers)
5241 {
5242 // split string parts for part of number/symbol that will be drawn normally and part that will be drawn as exponent:
5243 basePart = text.left(ePos);
5244 // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base:
5245 if (mScaleType == stLogarithmic && basePart == "1")
5246 basePart = "10";
5247 else
5248 basePart += (mNumberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + "10";
5249 expPart = text.mid(ePos+1);
5250 // clip "+" and leading zeros off expPart:
5251 while (expPart.at(1) == '0' && expPart.length() > 2) // length > 2 so we leave one zero when numberFormatChar is 'e'
5252 expPart.remove(1, 1);
5253 if (expPart.at(0) == mPositiveSignChar)
5254 expPart.remove(0, 1);
5255 // prepare smaller font for exponent:
5256 expFont = font;
5257 expFont.setPointSize(expFont.pointSize()*0.75);
5258 // calculate bounding rects of base part, exponent part and total one:
5259 QFontMetrics baseFontMetrics(bugFixFont);
5260 baseBounds = baseFontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, basePart);
5261 QFontMetrics expFontMetrics(expFont);
5262 expBounds = expFontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip, expPart);
5263 bounds = baseBounds.adjusted(0, 0, expBounds.width(), 0);
5264 } else // useBeautifulPowers == false
5265 {
5266 QFontMetrics fontMetrics(bugFixFont);
5267 bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, text);
5268 }
5269
5270 // if rotated tick labels, transform bounding rect, too:
5271 QRect rotatedBounds = bounds;
5272 if (!qFuzzyIsNull(mTickLabelRotation))
5273 {
5274 QTransform transform;
5275 transform.rotate(mTickLabelRotation);
5276 rotatedBounds = transform.mapRect(bounds);
5277 }
5278
5279 // expand passed tickLabelsSize if current tick label is larger:
5280 if (rotatedBounds.width() > tickLabelsSize->width())
5281 tickLabelsSize->setWidth(rotatedBounds.width());
5282 if (rotatedBounds.height() > tickLabelsSize->height())
5283 tickLabelsSize->setHeight(rotatedBounds.height());
5284 }
5285
5286 /*! \internal
5287
5288 Handles the selection \a event and returns true when the selection event hit any parts of the
5289 axis. If the selection state of any parts of the axis was changed, the output parameter \a
5290 modified is set to true.
5291
5292 When \a additiveSelecton is true, any new selections become selected in addition to the recent
5293 selections. The recent selections are not cleared. Further, clicking on one object multiple times
5294 in additive selection mode, toggles the selection of that object on and off.
5295
5296 To indicate that an event deselects the axis (i.e. the parts that are deselectable by the user,
5297 see \ref setSelectable), pass 0 as \a event.
5298 */
5299 bool QCPAxis::handleAxisSelection(QMouseEvent *event, bool additiveSelection, bool &modified)
5300 {
5301 bool selectionFound = false;
5302 if (event)
5303 {
5304 SelectablePart selectedAxisPart = selectTest(event->pos());
5305 if (selectedAxisPart == spNone || !selectable().testFlag(selectedAxisPart))
5306 {
5307 // deselect parts that are changeable (selectable):
5308 SelectableParts newState = selected() & ~selectable();
5309 if (newState != selected() && !additiveSelection)
5310 {
5311 modified = true;
5312 setSelected(newState);
5313 }
5314 } else
5315 {
5316 selectionFound = true;
5317 if (additiveSelection)
5318 {
5319 // additive selection, so toggle selected part:
5320 setSelected(selected() ^ selectedAxisPart);
5321 modified = true;
5322 } else
5323 {
5324 // not additive selection, so select part and deselect all others that are changeable (selectable):
5325 SelectableParts newState = (selected() & ~selectable()) | selectedAxisPart;
5326 if (newState != selected())
5327 {
5328 modified = true;
5329 setSelected(newState);
5330 }
5331 }
5332 }
5333 } else // event == 0, so deselect all changeable parts
5334 {
5335 SelectableParts newState = selected() & ~selectable();
5336 if (newState != selected())
5337 {
5338 modified = true;
5339 setSelected(newState);
5340 }
5341 }
5342 return selectionFound;
5343 }
5344
5345 /*! \internal
5346
5347 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
5348 before drawing axis lines.
5349
5350 This is the antialiasing state the painter passed to the \ref draw method is in by default.
5351
5352 This function takes into account the local setting of the antialiasing flag as well as
5353 the overrides set e.g. with \ref QCustomPlot::setNotAntialiasedElements.
5354
5355 \see setAntialiased
5356 */
5357 void QCPAxis::applyDefaultAntialiasingHint(QCPPainter *painter) const
5358 {
5359 applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes);
5360 }
5361
5362 /*! \internal
5363
5364 Returns via \a lowIndex and \a highIndex, which ticks in the current tick vector are visible in
5365 the current range. The return values are indices of the tick vector, not the positions of the
5366 ticks themselves.
5367
5368 The actual use of this function is when we have an externally provided tick vector, which might
5369 exceed far beyond the currently displayed range, and would cause unnecessary calculations e.g. of
5370 subticks.
5371 */
5372 void QCPAxis::visibleTickBounds(int &lowIndex, int &highIndex) const
5373 {
5374 lowIndex = 0;
5375 highIndex = -1;
5376 // make sure only ticks that are in visible range are returned
5377 for (int i=0; i < mTickVector.size(); ++i)
5378 {
5379 lowIndex = i;
5380 if (mTickVector.at(i) >= mRange.lower) break;
5381 }
5382 for (int i=mTickVector.size()-1; i >= 0; --i)
5383 {
5384 highIndex = i;
5385 if (mTickVector.at(i) <= mRange.upper) break;
5386 }
5387 }
5388
5389 /*! \internal
5390
5391 A log function with the base mScaleLogBase, used mostly for coordinate transforms in logarithmic
5392 scales with arbitrary log base. Uses the buffered mScaleLogBaseLogInv for faster calculation.
5393 This is set to <tt>1.0/qLn(mScaleLogBase)</tt> in \ref setScaleLogBase.
5394
5395 \see basePow, setScaleLogBase, setScaleType
5396 */
5397 double QCPAxis::baseLog(double value) const
5398 {
5399 return qLn(value)*mScaleLogBaseLogInv;
5400 }
5401
5402 /*! \internal
5403
5404 A power function with the base mScaleLogBase, used mostly for coordinate transforms in
5405 logarithmic scales with arbitrary log base.
5406
5407 \see baseLog, setScaleLogBase, setScaleType
5408 */
5409 double QCPAxis::basePow(double value) const
5410 {
5411 return qPow(mScaleLogBase, value);
5412 }
5413
5414 /*! \internal
5415
5416 Returns the pen that is used to draw the axis base line. Depending on the selection state, this
5417 is either mSelectedBasePen or mBasePen.
5418 */
5419 QPen QCPAxis::getBasePen() const
5420 {
5421 return mSelected.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
5422 }
5423
5424 /*! \internal
5425
5426 Returns the pen that is used to draw the (major) ticks. Depending on the selection state, this
5427 is either mSelectedTickPen or mTickPen.
5428 */
5429 QPen QCPAxis::getTickPen() const
5430 {
5431 return mSelected.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
5432 }
5433
5434 /*! \internal
5435
5436 Returns the pen that is used to draw the subticks. Depending on the selection state, this
5437 is either mSelectedSubTickPen or mSubTickPen.
5438 */
5439 QPen QCPAxis::getSubTickPen() const
5440 {
5441 return mSelected.testFlag(spAxis) ? mSelectedSubTickPen : mSubTickPen;
5442 }
5443
5444 /*! \internal
5445
5446 Returns the font that is used to draw the tick labels. Depending on the selection state, this
5447 is either mSelectedTickLabelFont or mTickLabelFont.
5448 */
5449 QFont QCPAxis::getTickLabelFont() const
5450 {
5451 return mSelected.testFlag(spTickLabels) ? mSelectedTickLabelFont : mTickLabelFont;
5452 }
5453
5454 /*! \internal
5455
5456 Returns the font that is used to draw the axis label. Depending on the selection state, this
5457 is either mSelectedLabelFont or mLabelFont.
5458 */
5459 QFont QCPAxis::getLabelFont() const
5460 {
5461 return mSelected.testFlag(spAxisLabel) ? mSelectedLabelFont : mLabelFont;
5462 }
5463
5464 /*! \internal
5465
5466 Returns the color that is used to draw the tick labels. Depending on the selection state, this
5467 is either mSelectedTickLabelColor or mTickLabelColor.
5468 */
5469 QColor QCPAxis::getTickLabelColor() const
5470 {
5471 return mSelected.testFlag(spTickLabels) ? mSelectedTickLabelColor : mTickLabelColor;
5472 }
5473
5474 /*! \internal
5475
5476 Returns the color that is used to draw the axis label. Depending on the selection state, this
5477 is either mSelectedLabelColor or mLabelColor.
5478 */
5479 QColor QCPAxis::getLabelColor() const
5480 {
5481 return mSelected.testFlag(spAxisLabel) ? mSelectedLabelColor : mLabelColor;
5482 }
5483
5484 /*! \internal
5485
5486 Simulates the steps of \ref draw by calculating all appearing text bounding boxes. From this
5487 information, the appropriate margin for this axis is determined, so nothing is drawn beyond the
5488 widget border in the actual \ref draw function (if \ref QCustomPlot::setAutoMargin is set to
5489 true).
5490
5491 The margin consists of: tick label padding, tick label size, label padding, label size. The
5492 return value is the calculated margin for this axis. Thus, an axis with axis type \ref atLeft
5493 will return an appropriate left margin, \ref atBottom will return an appropriate bottom margin
5494 and so forth.
5495
5496 \warning if anything is changed in this function, make sure it's synchronized with the actual
5497 drawing function \ref draw.
5498 */
5499 int QCPAxis::calculateMargin() const
5500 {
5501 // run through similar steps as QCPAxis::draw, and caluclate margin needed to fit axis and its labels
5502 int margin = 0;
5503
5504 if (mVisible)
5505 {
5506 int lowTick, highTick;
5507 visibleTickBounds(lowTick, highTick);
5508 // get length of tick marks reaching outside axis rect:
5509 margin += qMax(0, qMax(mTickLengthOut, mSubTickLengthOut));
5510 // calculate size of tick labels:
5511 QSize tickLabelsSize(0, 0);
5512 if (mTickLabels)
5513 {
5514 for (int i=lowTick; i <= highTick; ++i)
5515 {
5516 getMaxTickLabelSize(mTickLabelFont, mTickVectorLabels.at(i), &tickLabelsSize); // don't use getTickLabelFont() because we don't want margin to possibly change on selection
5517 }
5518 if (orientation() == Qt::Horizontal)
5519 margin += tickLabelsSize.height() + mTickLabelPadding;
5520 else
5521 margin += tickLabelsSize.width() + mTickLabelPadding;
5522 }
5523 // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):
5524 if (!mLabel.isEmpty())
5525 {
5526 QFontMetrics fontMetrics(mLabelFont); // don't use getLabelFont() because we don't want margin to possibly change on selection
5527 QRect bounds;
5528 bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, mLabel);
5529 margin += bounds.height() + mLabelPadding;
5530 }
5531 }
5532 margin += mPadding;
5533
5534 if (margin < 15) // need a bit of margin if no axis text is shown at all (i.e. only baseline and tick lines, or no axis at all)
5535 margin = 15;
5536 return margin;
5537 }
5538
5539
5540 // ================================================================================
5541 // =================== QCustomPlot
5542 // ================================================================================
5543
5544 /*! \class QCustomPlot
5545 \brief The central class of the library, the QWidget which displays the plot and interacts with the user.
5546
5547 For tutorials on how to use QCustomPlot, see the website\n
5548 http://www.WorksLikeClockWork.com/index.php/components/qt-plotting-widget
5549 */
5550
5551 /* start of documentation of inline functions */
5552
5553 /*! \fn QRect QCustomPlot::viewport() const
5554
5555 Returns the viewport rect of this QCustomPlot instance. The viewport is the area the plot is
5556 drawn in, all mechanisms, e.g. margin caluclation take the viewport to be the outer border of the
5557 plot. The viewport normally is the rect() of the QCustomPlot widget, i.e. a rect with top left
5558 (0, 0) and size of the QCustomPlot widget.
5559
5560 Don't confuse the viewport with the axisRect. An axisRect is the rect defined by two axes, where
5561 the graphs/plottables are drawn in. The viewport is larger and contains also the axes themselves, their
5562 tick numbers, their labels, the plot title etc.
5563
5564 Only when saving to a file (see \ref savePng, savePdf etc.) the viewport is temporarily modified
5565 to allow saving plots with sizes independent of the current widget size.
5566 */
5567
5568 /* end of documentation of inline functions */
5569 /* start of documentation of signals */
5570
5571 /*! \fn void QCustomPlot::mouseDoubleClick(QMouseEvent *event)
5572
5573 This signal is emitted when the QCustomPlot receives a mouse double click event.
5574 */
5575
5576 /*! \fn void QCustomPlot::mousePress(QMouseEvent *event)
5577
5578 This signal is emitted when the QCustomPlot receives a mouse press event.
5579
5580 It is emitted before the QCustomPlot handles its range dragging mechanism, so a slot connected to
5581 this signal can still influence the behaviour e.g. with \ref setRangeDrag or \ref
5582 setRangeDragAxes.
5583 */
5584
5585 /*! \fn void QCustomPlot::mouseMove(QMouseEvent *event)
5586
5587 This signal is emitted when the QCustomPlot receives a mouse move event.
5588
5589 It is emitted before the QCustomPlot handles its range dragging mechanism, so a slot connected to
5590 this signal can still influence the behaviour e.g. with \ref setRangeDrag.
5591
5592 \warning It is discouraged to change the drag-axes with \ref setRangeDragAxes here, because the
5593 dragging starting point was saved the moment the mouse was pressed. Thus it only has a sensible
5594 meaning for the range drag axes that were set at that moment. If you want to change the drag
5595 axes, consider doing this in the \ref mousePress signal instead.
5596 */
5597
5598 /*! \fn void QCustomPlot::mouseRelease(QMouseEvent *event)
5599
5600 This signal is emitted when the QCustomPlot receives a mouse release event.
5601
5602 It is emitted before the QCustomPlot handles its selection mechanism, so a slot connected to this
5603 signal can still influence the behaviour e.g. with \ref setInteractions or \ref
5604 QCPAbstractPlottable::setSelectable.
5605 */
5606
5607 /*! \fn void QCustomPlot::mouseWheel(QMouseEvent *event)
5608
5609 This signal is emitted when the QCustomPlot receives a mouse wheel event.
5610
5611 It is emitted before the QCustomPlot handles its range zooming mechanism, so a slot connected to
5612 this signal can still influence the behaviour e.g. with \ref setRangeZoom, \ref setRangeZoomAxes
5613 or \ref setRangeZoomFactor.
5614 */
5615
5616 /*! \fn void QCustomPlot::plottableClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
5617
5618 This signal is emitted when a plottable is clicked.
5619
5620 \a event is the mouse event that caused the click and \a plottable is the plottable that received
5621 the click.
5622
5623 \see plottableDoubleClick
5624 */
5625
5626 /*! \fn void QCustomPlot::plottableDoubleClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
5627
5628 This signal is emitted when a plottable is double clicked.
5629
5630 \a event is the mouse event that caused the click and \a plottable is the plottable that received
5631 the click.
5632
5633 \see plottableClick
5634 */
5635
5636 /*! \fn void QCustomPlot::itemClick(QCPAbstractItem *item, QMouseEvent *event)
5637
5638 This signal is emitted when an item is clicked.
5639
5640 \a event is the mouse event that caused the click and \a item is the item that received the
5641 click.
5642
5643 \see itemDoubleClick
5644 */
5645
5646 /*! \fn void QCustomPlot::itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event)
5647
5648 This signal is emitted when an item is double clicked.
5649
5650 \a event is the mouse event that caused the click and \a item is the item that received the
5651 click.
5652
5653 \see itemClick
5654 */
5655
5656 /*! \fn void QCustomPlot::axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
5657
5658 This signal is emitted when an axis is clicked.
5659
5660 \a event is the mouse event that caused the click, \a axis is the axis that received the click and
5661 \a part indicates the part of the axis that was clicked.
5662
5663 \see axisDoubleClick
5664 */
5665
5666 /*! \fn void QCustomPlot::axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
5667
5668 This signal is emitted when an axis is double clicked.
5669
5670 \a event is the mouse event that caused the click, \a axis is the axis that received the click and
5671 \a part indicates the part of the axis that was clicked.
5672
5673 \see axisClick
5674 */
5675
5676 /*! \fn void QCustomPlot::legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
5677
5678 This signal is emitted when a legend (item) is clicked.
5679
5680 \a event is the mouse event that caused the click, \a legend is the legend that received the
5681 click and \a item is the legend item that received the click. If only the legend and no item is
5682 clicked, \a item is 0 (e.g. a click inside the legend padding, which is not part of any item).
5683
5684 \see legendDoubleClick
5685 */
5686
5687 /*! \fn void QCustomPlot::legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
5688
5689 This signal is emitted when a legend (item) is double clicked.
5690
5691 \a event is the mouse event that caused the click, \a legend is the legend that received the
5692 click and \a item is the legend item that received the click. If only the legend and no item is
5693 clicked, \a item is 0 (e.g. a click inside the legend padding, which is not part of any item).
5694
5695 \see legendClick
5696 */
5697
5698 /*! \fn void QCustomPlot:: titleClick(QMouseEvent *event)
5699
5700 This signal is emitted when the plot title is clicked.
5701
5702 \a event is the mouse event that caused the click.
5703
5704 \see titleDoubleClick
5705 */
5706
5707 /*! \fn void QCustomPlot::titleDoubleClick(QMouseEvent *event)
5708
5709 This signal is emitted when the plot title is double clicked.
5710
5711 \a event is the mouse event that caused the click.
5712
5713 \see titleClick
5714 */
5715
5716 /*! \fn void QCustomPlot::selectionChangedByUser()
5717
5718 This signal is emitted after the user has changed the selection in the QCustomPlot, e.g. by
5719 clicking. It is not emitted, when the selection state of an object has changed programmatically,
5720 e.g. by a direct call to setSelected() on a plottable or by calling \ref deselectAll.
5721
5722 See the documentation of \ref setInteractions for how to find out which objects are currently
5723 selected.
5724
5725 \see setInteractions, QCPAbstractPlottable::selectionChanged, QCPAxis::selectionChanged
5726 */
5727
5728 /*! \fn void QCustomPlot::beforeReplot()
5729
5730 This signal is emitted immediately before a replot takes place (caused by a call to the slot \ref
5731 replot).
5732
5733 It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them
5734 replot synchronously (i.e. it won't cause an infinite recursion).
5735
5736 \see replot, afterReplot
5737 */
5738
5739 /*! \fn void QCustomPlot::afterReplot()
5740
5741 This signal is emitted immediately after a replot has taken place (caused by a call to the slot \ref
5742 replot).
5743
5744 It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them
5745 replot synchronously (i.e. it won't cause an infinite recursion).
5746
5747 \see replot, beforeReplot
5748 */
5749
5750 /* end of documentation of signals */
5751
5752 /*!
5753 Constructs a QCustomPlot and sets reasonable default values.
5754 Four axes are created at the bottom, left, top and right sides (xAxis, yAxis, xAxis2, yAxis2),
5755 however, only the bottom and left axes are set to be visible.
5756 The legend is also set to be invisible initially.
5757 */
5758 QCustomPlot::QCustomPlot(QWidget *parent) :
5759 QWidget(parent),
5760 mDragging(false),
5761 mReplotting(false),
5762 mPlottingHints(QCP::phNone)
5763 {
5764 setAttribute(Qt::WA_NoMousePropagation);
5765 setAttribute(Qt::WA_OpaquePaintEvent);
5766 setMouseTracking(true);
5767 QLocale currentLocale = locale();
5768 currentLocale.setNumberOptions(QLocale::OmitGroupSeparator);
5769 setLocale(currentLocale);
5770
5771 // create very first layers:
5772 QCPLayer *gridLayer = new QCPLayer(this, "grid");
5773 QCPLayer *mainLayer = new QCPLayer(this, "main");
5774 QCPLayer *axesLayer = new QCPLayer(this, "axes");
5775 mLayers.append(gridLayer);
5776 mLayers.append(mainLayer);
5777 mLayers.append(axesLayer);
5778 setCurrentLayer(mainLayer);
5779
5780 mPaintBuffer = QPixmap(size());
5781 legend = new QCPLegend(this);
5782 legend->setVisible(false);
5783 legend->setLayer(axesLayer);
5784 xAxis = new QCPAxis(this, QCPAxis::atBottom);
5785 yAxis = new QCPAxis(this, QCPAxis::atLeft);
5786 xAxis2 = new QCPAxis(this, QCPAxis::atTop);
5787 yAxis2 = new QCPAxis(this, QCPAxis::atRight);
5788 xAxis2->setGrid(false);
5789 yAxis2->setGrid(false);
5790 xAxis2->setZeroLinePen(Qt::NoPen);
5791 yAxis2->setZeroLinePen(Qt::NoPen);
5792 xAxis2->setVisible(false);
5793 yAxis2->setVisible(false);
5794 xAxis->setLayer(axesLayer);
5795 yAxis->setLayer(axesLayer);
5796 xAxis2->setLayer(axesLayer);
5797 yAxis2->setLayer(axesLayer);
5798 xAxis->mGrid->setLayer(gridLayer);
5799 yAxis->mGrid->setLayer(gridLayer);
5800 xAxis2->mGrid->setLayer(gridLayer);
5801 yAxis2->mGrid->setLayer(gridLayer);
5802 mViewport = rect();
5803
5804 setNoAntialiasingOnDrag(false);
5805 setAutoAddPlottableToLegend(true);
5806 setAxisBackgroundScaled(true);
5807 setAxisBackgroundScaledMode(Qt::KeepAspectRatioByExpanding);
5808 setTitleFont(QFont(font().family(), 14, QFont::Bold));
5809 setTitleColor(Qt::black);
5810 setSelectedTitleFont(QFont(font().family(), 14, QFont::Bold));
5811 setSelectedTitleColor(Qt::blue);
5812 setTitleSelected(false);
5813 setTitle("");
5814 setColor(Qt::white);
5815
5816 #ifdef Q_WS_WIN
5817 setPlottingHint(QCP::phForceRepaint);
5818 #endif
5819 setAntialiasedElements(QCP::aeNone);
5820 setNotAntialiasedElements(QCP::aeNone);
5821 setInteractions(iRangeDrag|iRangeZoom);
5822 setMultiSelectModifier(Qt::ControlModifier);
5823 setRangeDragAxes(xAxis, yAxis);
5824 setRangeZoomAxes(xAxis, yAxis);
5825 setRangeDrag(0);
5826 setRangeZoom(0);
5827 setRangeZoomFactor(0.85);
5828 setSelectionTolerance(8);
5829
5830 setMargin(0, 0, 0, 0); // also initializes the mAxisRect
5831 setAutoMargin(true);
5832 replot();
5833 }
5834
5835 QCustomPlot::~QCustomPlot()
5836 {
5837 clearPlottables();
5838 clearItems();
5839 delete legend;
5840 delete xAxis;
5841 delete yAxis;
5842 delete xAxis2;
5843 delete yAxis2;
5844 qDeleteAll(mLayers);
5845 mLayers.clear();
5846 }
5847
5848 /*!
5849 Returns the range drag axis of the \a orientation provided
5850 \see setRangeDragAxes
5851 */
5852 QCPAxis *QCustomPlot::rangeDragAxis(Qt::Orientation orientation)
5853 {
5854 return (orientation == Qt::Horizontal ? mRangeDragHorzAxis : mRangeDragVertAxis);
5855 }
5856
5857 /*!
5858 Returns the range zoom axis of the \a orientation provided
5859 \see setRangeZoomAxes
5860 */
5861 QCPAxis *QCustomPlot::rangeZoomAxis(Qt::Orientation orientation)
5862 {
5863 return (orientation == Qt::Horizontal ? mRangeZoomHorzAxis : mRangeZoomVertAxis);
5864 }
5865
5866 /*!
5867 Returns the range zoom factor of the \a orientation provided
5868 \see setRangeZoomFactor
5869 */
5870 double QCustomPlot::rangeZoomFactor(Qt::Orientation orientation)
5871 {
5872 return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert);
5873 }
5874
5875 /*!
5876 Sets the plot title which will be drawn centered at the top of the widget.
5877 The title position is not dependant on the actual position of the axes. However, if
5878 \ref setAutoMargin is set to true, the top margin will be adjusted appropriately,
5879 so the top axis labels/tick labels will not overlap with the title.
5880
5881 \see setTitleFont, setTitleColor
5882 */
5883 void QCustomPlot::setTitle(const QString &title)
5884 {
5885 mTitle = title;
5886 }
5887
5888 /*!
5889 Sets the font of the plot title
5890 \see setTitleColor, setTitle
5891 */
5892 void QCustomPlot::setTitleFont(const QFont &font)
5893 {
5894 mTitleFont = font;
5895 }
5896
5897 /*!
5898 Sets the text color of the plot title
5899 \see setTitleFont, setTitle
5900 */
5901 void QCustomPlot::setTitleColor(const QColor &color)
5902 {
5903 mTitleColor = color;
5904 }
5905
5906 /*!
5907 An alternative way to set the margins, by directly setting the wanted axis rect. The rect
5908 will be translated into appropriate margin values.
5909
5910 \warning Setting the axis rect with this function does not guarantee that the axis rect will stay
5911 like this indefinitely. In QCustomPlot, margins are the fixed values (if \ref setAutoMargin is
5912 false). Hence the axis rect is automatically changed when the widget size changes, but the
5913 margins (distances between axis rect sides and widget/viewport rect sides) stay the same.
5914
5915 \see setMargin
5916 */
5917 void QCustomPlot::setAxisRect(const QRect &arect)
5918 {
5919 mMarginLeft = arect.left()-mViewport.left();
5920 mMarginRight = mViewport.right()-arect.right();
5921 mMarginTop = arect.top()-mViewport.top();
5922 mMarginBottom = mViewport.bottom()-arect.bottom();
5923 updateAxisRect();
5924 }
5925
5926 /*!
5927 Sets the left margin manually. Will only have effect, if \ref setAutoMargin is set to false.
5928 see \ref setMargin for an explanation of what margins mean in QCustomPlot.
5929 */
5930 void QCustomPlot::setMarginLeft(int margin)
5931 {
5932 mMarginLeft = margin;
5933 updateAxisRect();
5934 }
5935
5936 /*!
5937 Sets the right margin manually. Will only have effect, if \ref setAutoMargin is set to false.
5938 see \ref setMargin for an explanation of what margins mean in QCustomPlot.
5939 */
5940 void QCustomPlot::setMarginRight(int margin)
5941 {
5942 mMarginRight = margin;
5943 updateAxisRect();
5944 }
5945
5946 /*!
5947 Sets the top margin manually. Will only have effect, if \ref setAutoMargin is set to false.
5948 see \ref setMargin for an explanation of what margins mean in QCustomPlot.
5949 */
5950 void QCustomPlot::setMarginTop(int margin)
5951 {
5952 mMarginTop = margin;
5953 updateAxisRect();
5954 }
5955
5956 /*!
5957 Sets the bottom margin manually. Will only have effect, if \ref setAutoMargin is set to false.
5958 see \ref setMargin for an explanation of what margins mean in QCustomPlot.
5959 */
5960 void QCustomPlot::setMarginBottom(int margin)
5961 {
5962 mMarginBottom = margin;
5963 updateAxisRect();
5964 }
5965
5966 /*!
5967 Sets the margins manually. Will only have effect, if \ref setAutoMargin is set to false.
5968 The margins are the distances in pixels between the axes box and the viewport box.
5969 The viewport box normally is the entire QCustomPlot widget or the entire image, if
5970 using one of the export functions. Positive margin values always mean the axes box
5971 is shrinked, going inward from the sides of the viewport box.
5972 */
5973 void QCustomPlot::setMargin(int left, int right, int top, int bottom)
5974 {
5975 mMarginLeft = left;
5976 mMarginRight = right;
5977 mMarginTop = top;
5978 mMarginBottom = bottom;
5979 updateAxisRect();
5980 }
5981
5982 /*!
5983 Sets whether the margins are calculated automatically depeding on the sizes
5984 of the tick labels, axis labels, paddings etc.
5985 If disabled, the margins must be set manually with the \a setMargin functions.
5986 \see setMargin, QCPAxis::setLabelPadding, QCPAxis::setTickLabelPadding
5987 */
5988 void QCustomPlot::setAutoMargin(bool enabled)
5989 {
5990 mAutoMargin = enabled;
5991 }
5992
5993 /*!
5994 Sets the background color of the QCustomPlot widget.
5995 */
5996 void QCustomPlot::setColor(const QColor &color)
5997 {
5998 mColor = color;
5999 }
6000
6001 /*!
6002 Sets which axis orientation may be range dragged by the user with mouse interaction.
6003 What orientation corresponds to which specific axis can be set with
6004 \ref setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical). By
6005 default, the horizontal axis is the bottom axis (xAxis) and the vertical axis
6006 is the left axis (yAxis).
6007
6008 To disable range dragging entirely, pass 0 as \a orientations or remove \ref iRangeDrag from \ref
6009 setInteractions. To enable range dragging for both directions, pass <tt>Qt::Horizontal |
6010 Qt::Vertical</tt> as \a orientations.
6011
6012 In addition to setting \a orientations to a non-zero value, make sure \ref setInteractions
6013 contains \ref iRangeDrag to enable the range dragging interaction.
6014
6015 \see setRangeZoom, setRangeDragAxes, setNoAntialiasingOnDrag
6016 */
6017 void QCustomPlot::setRangeDrag(Qt::Orientations orientations)
6018 {
6019 mRangeDrag = orientations;
6020 }
6021
6022 /*!
6023 Sets which axis orientation may be zoomed by the user with the mouse wheel. What orientation
6024 corresponds to which specific axis can be set with \ref setRangeZoomAxes(QCPAxis *horizontal,
6025 QCPAxis *vertical). By default, the horizontal axis is the bottom axis (xAxis) and the vertical
6026 axis is the left axis (yAxis).
6027
6028 To disable range zooming entirely, pass 0 as \a orientations or remove \ref iRangeZoom from \ref
6029 setInteractions. To enable range zooming for both directions, pass <tt>Qt::Horizontal |
6030 Qt::Vertical</tt> as \a orientations.
6031
6032 In addition to setting \a orientations to a non-zero value, make sure \ref setInteractions
6033 contains \ref iRangeZoom to enable the range zooming interaction.
6034
6035 \see setRangeZoomFactor, setRangeZoomAxes, setRangeDrag
6036 */
6037 void QCustomPlot::setRangeZoom(Qt::Orientations orientations)
6038 {
6039 mRangeZoom = orientations;
6040 }
6041
6042 /*!
6043 Sets the axes whose range will be dragged when \ref setRangeDrag enables mouse range dragging
6044 on the QCustomPlot widget.
6045
6046 \see setRangeZoomAxes
6047 */
6048 void QCustomPlot::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
6049 {
6050 if (horizontal)
6051 mRangeDragHorzAxis = horizontal;
6052 if (vertical)
6053 mRangeDragVertAxis = vertical;
6054 }
6055
6056 /*!
6057 Sets the axes whose range will be zoomed when \ref setRangeZoom enables mouse wheel zooming on the
6058 QCustomPlot widget. The two axes can be zoomed with different strengths, when different factors
6059 are passed to \ref setRangeZoomFactor(double horizontalFactor, double verticalFactor).
6060
6061 \see setRangeDragAxes
6062 */
6063 void QCustomPlot::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
6064 {
6065 if (horizontal)
6066 mRangeZoomHorzAxis = horizontal;
6067 if (vertical)
6068 mRangeZoomVertAxis = vertical;
6069 }
6070
6071 /*!
6072 Sets how strong one rotation step of the mouse wheel zooms, when range zoom was activated with
6073 \ref setRangeZoom. The two parameters \a horizontalFactor and \a verticalFactor provide a way to
6074 let the horizontal axis zoom at different rates than the vertical axis. Which axis is horizontal
6075 and which is vertical, can be set with \ref setRangeZoomAxes.
6076
6077 When the zoom factor is greater than one, scrolling the mouse wheel backwards (towards the user)
6078 will zoom in (make the currently visible range smaller). For zoom factors smaller than one, the
6079 same scrolling direction will zoom out.
6080 */
6081 void QCustomPlot::setRangeZoomFactor(double horizontalFactor, double verticalFactor)
6082 {
6083 mRangeZoomFactorHorz = horizontalFactor;
6084 mRangeZoomFactorVert = verticalFactor;
6085 }
6086
6087 /*! \overload
6088
6089 Sets both the horizontal and vertical zoom \a factor.
6090 */
6091 void QCustomPlot::setRangeZoomFactor(double factor)
6092 {
6093 mRangeZoomFactorHorz = factor;
6094 mRangeZoomFactorVert = factor;
6095 }
6096
6097 /*!
6098 Sets which elements are forcibly drawn antialiased as an or combination of QCP::AntialiasedElement.
6099
6100 This overrides the antialiasing settings for whole element groups, normally controlled with the
6101 \a setAntialiasing function on the individual elements. If an element is neither specified in
6102 \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on
6103 each individual element instance is used.
6104
6105 For example, if \a antialiasedElements contains \ref QCP::aePlottables, all plottables will be
6106 drawn antialiased, no matter what the specific QCPAbstractPlottable::setAntialiased value was set
6107 to.
6108
6109 \see setNotAntialiasedElements
6110 */
6111 void QCustomPlot::setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
6112 {
6113 mAntialiasedElements = antialiasedElements;
6114
6115 // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
6116 if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
6117 mNotAntialiasedElements |= ~mAntialiasedElements;
6118 }
6119
6120 /*!
6121 Sets whether the specified \a antialiasedElement is forcibly drawn antialiased.
6122
6123 This overrides the antialiasing settings for whole element groups, normally controlled with the
6124 \a setAntialiasing function on the individual elements. If an element is neither specified in
6125 \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on
6126 each individual element instance is used.
6127
6128 For example, if \a enabled is true and \a antialiasedElement is \ref QCP::aePlottables, all
6129 plottables will be drawn antialiased, no matter what the specific
6130 QCPAbstractPlottable::setAntialiased value was set to.
6131
6132 \see setNotAntialiasedElement
6133 */
6134 void QCustomPlot::setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled)
6135 {
6136 if (!enabled && mAntialiasedElements.testFlag(antialiasedElement))
6137 mAntialiasedElements &= ~antialiasedElement;
6138 else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement))
6139 mAntialiasedElements |= antialiasedElement;
6140
6141 // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
6142 if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
6143 mNotAntialiasedElements |= ~mAntialiasedElements;
6144 }
6145
6146 /*!
6147 Sets which elements are forcibly drawn not antialiased as an or combination of
6148 QCP::AntialiasedElement.
6149
6150 This overrides the antialiasing settings for whole element groups, normally controlled with the
6151 \a setAntialiasing function on the individual elements. If an element is neither specified in
6152 \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on
6153 each individual element instance is used.
6154
6155 For example, if \a notAntialiasedElements contains \ref QCP::aePlottables, no plottables will be
6156 drawn antialiased, no matter what the specific QCPAbstractPlottable::setAntialiased value was set
6157 to.
6158
6159 if an element in \a notAntialiasedElements is already set in \ref setAntialiasedElements, it is
6160 removed from there.
6161
6162 \see setAntialiasedElements
6163 */
6164 void QCustomPlot::setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements)
6165 {
6166 mNotAntialiasedElements = notAntialiasedElements;
6167
6168 // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
6169 if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
6170 mAntialiasedElements |= ~mNotAntialiasedElements;
6171 }
6172
6173 /*!
6174 Sets whether the specified \a notAntialiasedElement is forcibly drawn not antialiased.
6175
6176 This overrides the antialiasing settings for whole element groups, normally controlled with the
6177 \a setAntialiasing function on the individual elements. If an element is neither specified in
6178 \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on
6179 each individual element instance is used.
6180
6181 For example, if \a enabled is true and \a notAntialiasedElement is \ref QCP::aePlottables, no
6182 plottables will be drawn antialiased, no matter what the specific
6183 QCPAbstractPlottable::setAntialiased value was set to.
6184
6185 if \a enabled is true and \a notAntialiasedElement is already set with \ref
6186 setAntialiasedElement, it is removed from there.
6187
6188 \see setAntialiasedElement
6189 */
6190 void QCustomPlot::setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled)
6191 {
6192 if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement))
6193 mNotAntialiasedElements &= ~notAntialiasedElement;
6194 else if (enabled && !mNotAntialiasedElements.testFlag(notAntialiasedElement))
6195 mNotAntialiasedElements |= notAntialiasedElement;
6196
6197 // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
6198 if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
6199 mAntialiasedElements |= ~mNotAntialiasedElements;
6200 }
6201
6202 /*!
6203 If set to true, adding a plottable (e.g. a graph) to the QCustomPlot automatically also adds the
6204 newly created plottable to the legend.
6205
6206 \see addPlottable, addGraph, QCPLegend::addItem
6207 */
6208 void QCustomPlot::setAutoAddPlottableToLegend(bool on)
6209 {
6210 mAutoAddPlottableToLegend = on;
6211 }
6212
6213 /*!
6214 Sets \a pm as the axis background pixmap. The axis background pixmap will be drawn inside the current
6215 axis rect, before anything else (e.g. the axes themselves, grids, graphs, etc.) is drawn.
6216 If the provided pixmap doesn't have the same size as the axis rect, scaling can be enabled with \ref setAxisBackgroundScaled
6217 and the scaling mode (i.e. whether and how the aspect ratio is preserved) can be set with \ref setAxisBackgroundScaledMode.
6218 To set all these options in one call, consider using the overloaded version of this function.
6219 \see setAxisBackgroundScaled, setAxisBackgroundScaledMode
6220 */
6221 void QCustomPlot::setAxisBackground(const QPixmap &pm)
6222 {
6223 mAxisBackground = pm;
6224 mScaledAxisBackground = QPixmap();
6225 }
6226
6227 /*!
6228 \overload
6229 Allows setting the background pixmap, whether it shall be scaled and how it shall be scaled in one call.
6230 \see setAxisBackground(const QPixmap &pm), setAxisBackgroundScaled, setAxisBackgroundScaledMode
6231 */
6232 void QCustomPlot::setAxisBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
6233 {
6234 mAxisBackground = pm;
6235 mScaledAxisBackground = QPixmap();
6236 mAxisBackgroundScaled = scaled;
6237 mAxisBackgroundScaledMode = mode;
6238 }
6239
6240 /*!
6241 Sets whether the axis background pixmap shall be scaled to fit the current axis rect or not. If
6242 \a scaled is set to true, you may control whether and how the aspect ratio of the original pixmap is
6243 preserved with \ref setAxisBackgroundScaledMode.
6244
6245 Note that the scaled version of the original pixmap is buffered, so there is no performance penalty
6246 on replots, when enabling the scaling. (Except of course, the axis rect is continuously
6247 changed, but that's not very likely.)
6248
6249 \see setAxisBackground, setAxisBackgroundScaledMode
6250 */
6251 void QCustomPlot::setAxisBackgroundScaled(bool scaled)
6252 {
6253 mAxisBackgroundScaled = scaled;
6254 }
6255
6256 /*!
6257 If scaling of the axis background pixmap is enabled (\ref setAxisBackgroundScaled), use this function to
6258 define whether and how the aspect ratio of the original pixmap passed to \ref setAxisBackground is preserved.
6259 \see setAxisBackground, setAxisBackgroundScaled
6260 */
6261 void QCustomPlot::setAxisBackgroundScaledMode(Qt::AspectRatioMode mode)
6262 {
6263 mAxisBackgroundScaledMode = mode;
6264 }
6265
6266 /*!
6267 Sets the possible interactions of this QCustomPlot as an or-combination of \ref Interaction
6268 enums. There are the following types of interactions:
6269
6270 <b>Axis range manipulation</b> is controlled via \ref iRangeDrag and \ref iRangeZoom. When the
6271 respective interaction is enabled, the user may drag axes ranges and zoom with the mouse wheel.
6272 For details how to control which axes the user may drag/zoom and in what orientations, see \ref
6273 setRangeDrag, \ref setRangeZoom, \ref setRangeDragAxes, \ref setRangeZoomAxes.
6274
6275 <b>Plottable selection</b> is controlled by \ref iSelectPlottables. If \ref iSelectPlottables is
6276 set, the user may select plottables (e.g. graphs, curves, bars,...) by clicking on them or in
6277 their vicinity, see \ref setSelectionTolerance. Whether the user can actually select a plottable
6278 can further be restricted with the \ref QCPAbstractPlottable::setSelectable function on the
6279 specific plottable. To find out whether a specific plottable is selected, call
6280 QCPAbstractPlottable::selected(). To retrieve a list of all currently selected plottables, call
6281 \ref selectedPlottables. If you're only interested in QCPGraphs, you may use the convenience
6282 function \ref selectedGraphs.
6283
6284 <b>Item selection</b> is controlled by \ref iSelectItems. If \ref iSelectItems is set, the user
6285 may select items (e.g. QCPItemLine, QCPItemText,...) by clicking on them or in their vicinity. To
6286 find out whether a specific item is selected, call QCPAbstractItem::selected(). To retrieve a
6287 list of all currently selected items, call \ref selectedItems.
6288
6289 <b>Axis selection</b> is controlled with \ref iSelectAxes. If \ref iSelectAxes is set, the user
6290 may select parts of the axes by clicking on them. What parts exactly (e.g. Axis base line, tick
6291 labels, axis label) are selectable can be controlled via \ref QCPAxis::setSelectable for each
6292 axis. To retrieve a list of all axes that currently contain selected parts, call \ref
6293 selectedAxes. Which parts of an axis are selected, can be retrieved with QCPAxis::selected().
6294
6295 <b>Legend selection</b> is controlled with \ref iSelectLegend. If this is set, the user may
6296 select the legend itself or individual items by clicking on them. What parts exactly are
6297 selectable can be controlled via \ref QCPLegend::setSelectable. To find out whether the legend or
6298 any child items are selected, check the value of QCPLegend::selected. To find out which child
6299 items are selected, call \ref QCPLegend::selectedItems.
6300
6301 <b>Plot title selection</b> is controlled with \ref iSelectTitle. If set, the user may select the
6302 plot title by clicking on it. To find out whether the title is currently selected, call
6303 QCustomPlot::titleSelected().
6304
6305 If the selection state has changed by user interaction, the \ref selectionChangedByUser signal is
6306 emitted. Each selectable object additionally emits an individual selectionChanged signal whenever
6307 their selection state has changed, i.e. not only by user interaction.
6308
6309 To allow multiple objects to be selected by holding the modifier set with \ref
6310 setMultiSelectModifier, set the flag \ref iMultiSelect.
6311
6312 \note In addition to the selection mechanism presented here, QCustomPlot always emits
6313 corresponding signals, when an object is clicked or double clicked. see \ref plottableClick and
6314 \ref plottableDoubleClick for example.
6315
6316 \see setInteraction, setSelectionTolerance
6317 */
6318 void QCustomPlot::setInteractions(const Interactions &interactions)
6319 {
6320 mInteractions = interactions;
6321 }
6322
6323 /*!
6324 Sets the single \a interaction of this QCustomPlot to \a enabled.
6325
6326 For details about the interaction system, see \ref setInteractions.
6327
6328 \see setInteractions
6329 */
6330 void QCustomPlot::setInteraction(const QCustomPlot::Interaction &interaction, bool enabled)
6331 {
6332 if (!enabled && mInteractions.testFlag(interaction))
6333 mInteractions &= ~interaction;
6334 else if (enabled && !mInteractions.testFlag(interaction))
6335 mInteractions |= interaction;
6336 }
6337
6338 /*!
6339 Sets the tolerance that is used when deciding whether a click on the QCustomPlot surface selects
6340 an object (e.g. a plottable) or not.
6341
6342 If for example the user clicks in the vicinity of the line of a QCPGraph, it's only regarded as a
6343 potential selection when the minimum distance between the click position and the graph line is
6344 smaller than \a pixels. Objects that are defined by an area (e.g. QCPBars) only react to clicks
6345 directly inside the area and ignore this selection tolerance. In other words it only has meaning
6346 for parts of objects that are too thin to exactly hit with a click and thus need such a
6347 tolerance.
6348
6349 \see setInteractions, QCPAbstractPlottable::selectTest
6350 */
6351 void QCustomPlot::setSelectionTolerance(int pixels)
6352 {
6353 mSelectionTolerance = pixels;
6354 }
6355
6356 /*!
6357 This \a font is used to draw the title, when it is selected.
6358
6359 \see setTitleSelected, setTitleFont
6360 */
6361 void QCustomPlot::setSelectedTitleFont(const QFont &font)
6362 {
6363 mSelectedTitleFont = font;
6364 }
6365
6366 /*!
6367 This \a color is used to draw the title, when it is selected.
6368
6369 \see setTitleSelected, setTitleColor
6370 */
6371 void QCustomPlot::setSelectedTitleColor(const QColor &color)
6372 {
6373 mSelectedTitleColor = color;
6374 }
6375
6376 /*!
6377 Sets whether the plot title is selected.
6378
6379 \see setInteractions, setSelectedTitleFont, setSelectedTitleColor, setTitle
6380 */
6381 void QCustomPlot::setTitleSelected(bool selected)
6382 {
6383 mTitleSelected = selected;
6384 }
6385
6386 /*!
6387 Sets whether antialiasing is disabled for all elements while the user is dragging axes ranges. If
6388 many objects, especially plottables, are normally drawn antialiased, this greatly improves
6389 performance during dragging. Thus it creates a more responsive user experience. As soon as the
6390 user stops dragging, the last replot is done with normal antialiasing, to restore high image
6391 quality.
6392
6393 \see setAntialiasedElements, setNotAntialiasedElements
6394 */
6395 void QCustomPlot::setNoAntialiasingOnDrag(bool enabled)
6396 {
6397 mNoAntialiasingOnDrag = enabled;
6398 }
6399
6400 /*!
6401 Sets the plotting hints for this QCustomPlot instance.
6402 \see setPlottingHint
6403 */
6404 void QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints)
6405 {
6406 mPlottingHints = hints;
6407 }
6408
6409 /*!
6410 Sets the specified plotting \a hint to \a enabled.
6411 \see setPlottingHints
6412 */
6413 void QCustomPlot::setPlottingHint(QCP::PlottingHint hint, bool enabled)
6414 {
6415 QCP::PlottingHints newHints = mPlottingHints;
6416 if (!enabled)
6417 newHints &= ~hint;
6418 else
6419 newHints |= hint;
6420
6421 if (newHints != mPlottingHints)
6422 setPlottingHints(newHints);
6423 }
6424
6425 /*!
6426 Sets the keyboard modifier that will be recognized as multi-select-modifier.
6427
6428 If \ref iMultiSelect is specified in \ref setInteractions, the user may select multiple objects
6429 by clicking on them one after the other while holding down \a modifier.
6430
6431 By default the multi-select-modifier is set to Qt::ControlModifier.
6432
6433 \see setInteractions
6434 */
6435 void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier)
6436 {
6437 mMultiSelectModifier = modifier;
6438 }
6439
6440 /*!
6441 Returns the plottable with \a index. If the index is invalid, returns 0.
6442
6443 There is an overloaded version of this function with no parameter which returns the last added
6444 plottable, see QCustomPlot::plottable()
6445
6446 \see plottableCount, addPlottable
6447 */
6448 QCPAbstractPlottable *QCustomPlot::plottable(int index)
6449 {
6450 if (index >= 0 && index < mPlottables.size())
6451 {
6452 return mPlottables.at(index);
6453 } else
6454 {
6455 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
6456 return 0;
6457 }
6458 }
6459
6460 /*! \overload
6461
6462 Returns the last plottable, that was added with \ref addPlottable. If there are no plottables in the plot,
6463 returns 0.
6464
6465 \see plottableCount, addPlottable
6466 */
6467 QCPAbstractPlottable *QCustomPlot::plottable()
6468 {
6469 if (!mPlottables.isEmpty())
6470 {
6471 return mPlottables.last();
6472 } else
6473 return 0;
6474 }
6475
6476 /*!
6477 Adds the specified plottable to the plot and, if \ref setAutoAddPlottableToLegend is enabled, to the legend.
6478 QCustomPlot takes ownership of the plottable.
6479
6480 Returns true on success, i.e. when \a plottable wasn't already added to the plot and
6481 the parent plot of \a plottable is this QCustomPlot (the latter is controlled by what
6482 axes the plottable was passed in the constructor).
6483
6484 \see plottable, plottableCount, removePlottable, clearPlottables
6485 */
6486 bool QCustomPlot::addPlottable(QCPAbstractPlottable *plottable)
6487 {
6488 if (mPlottables.contains(plottable))
6489 {
6490 qDebug() << Q_FUNC_INFO << "plottable already added to this QCustomPlot:" << reinterpret_cast<quintptr>(plottable);
6491 return false;
6492 }
6493 if (plottable->parentPlot() != this)
6494 {
6495 qDebug() << Q_FUNC_INFO << "plottable not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(plottable);
6496 return false;
6497 }
6498
6499 mPlottables.append(plottable);
6500 // possibly add plottable to legend:
6501 if (mAutoAddPlottableToLegend)
6502 plottable->addToLegend();
6503 // special handling for QCPGraphs to maintain the simple graph interface:
6504 if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
6505 mGraphs.append(graph);
6506 if (!plottable->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor)
6507 plottable->setLayer(currentLayer());
6508 return true;
6509 }
6510
6511 /*!
6512 Removes the specified plottable from the plot and, if necessary, from the legend.
6513
6514 Returns true on success.
6515
6516 \see addPlottable, clearPlottables
6517 */
6518 bool QCustomPlot::removePlottable(QCPAbstractPlottable *plottable)
6519 {
6520 if (!mPlottables.contains(plottable))
6521 {
6522 qDebug() << Q_FUNC_INFO << "plottable not in list:" << reinterpret_cast<quintptr>(plottable);
6523 return false;
6524 }
6525
6526 // remove plottable from legend:
6527 plottable->removeFromLegend();
6528 // special handling for QCPGraphs to maintain the simple graph interface:
6529 if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
6530 mGraphs.removeOne(graph);
6531 // remove plottable:
6532 delete plottable;
6533 mPlottables.removeOne(plottable);
6534 return true;
6535 }
6536
6537 /*! \overload
6538
6539 Removes the plottable by its \a index.
6540 */
6541 bool QCustomPlot::removePlottable(int index)
6542 {
6543 if (index >= 0 && index < mPlottables.size())
6544 return removePlottable(mPlottables[index]);
6545 else
6546 {
6547 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
6548 return false;
6549 }
6550 }
6551
6552 /*!
6553 Removes all plottables from the plot (and the legend, if necessary).
6554
6555 Returns the number of plottables removed.
6556
6557 \see removePlottable
6558 */
6559 int QCustomPlot::clearPlottables()
6560 {
6561 int c = mPlottables.size();
6562 for (int i=c-1; i >= 0; --i)
6563 removePlottable(mPlottables[i]);
6564 return c;
6565 }
6566
6567 /*!
6568 Returns the number of currently existing plottables in the plot
6569
6570 \see plottable, addPlottable
6571 */
6572 int QCustomPlot::plottableCount() const
6573 {
6574 return mPlottables.size();
6575 }
6576
6577 /*!
6578 Returns a list of the selected plottables. If no plottables are currently selected, the list is empty.
6579
6580 There is a convenience function if you're only interested in selected graphs, see \ref selectedGraphs.
6581
6582 \see setInteractions, QCPAbstractPlottable::setSelectable, QCPAbstractPlottable::setSelected, selectedGraphs
6583 */
6584 QList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const
6585 {
6586 QList<QCPAbstractPlottable*> result;
6587 for (int i=0; i<mPlottables.size(); ++i)
6588 {
6589 if (mPlottables.at(i)->selected())
6590 result.append(mPlottables.at(i));
6591 }
6592 return result;
6593 }
6594
6595 /*!
6596 Returns the plottable at the pixel position \a pos. Plottables that only consist of single lines
6597 (e.g. graphs) have a tolerance band around them, see \ref setSelectionTolerance.
6598 If multiple plottables come into consideration, the one closest to \a pos is returned.
6599
6600 If \a onlySelectable is true, only plottables that are selectable
6601 (QCPAbstractPlottable::setSelectable) are considered.
6602
6603 If there is no plottable at \a pos, the return value is 0.
6604 */
6605 QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable) const
6606 {
6607 QCPAbstractPlottable *resultPlottable = 0;
6608 double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
6609
6610 for (int i=0; i<mPlottables.size(); ++i)
6611 {
6612 QCPAbstractPlottable *currentPlottable = mPlottables[i];
6613 if (onlySelectable && !currentPlottable->selectable())
6614 continue;
6615 if ((currentPlottable->keyAxis()->axisRect() | currentPlottable->valueAxis()->axisRect()).contains(pos.toPoint())) // only consider clicks inside the rect that is spanned by the plottable's key/value axes
6616 {
6617 double currentDistance = currentPlottable->selectTest(pos);
6618 if (currentDistance >= 0 && currentDistance < resultDistance)
6619 {
6620 resultPlottable = currentPlottable;
6621 resultDistance = currentDistance;
6622 }
6623 }
6624 }
6625
6626 return resultPlottable;
6627 }
6628
6629 /*!
6630 Returns whether this QCustomPlot instance contains the \a plottable.
6631
6632 \see addPlottable
6633 */
6634 bool QCustomPlot::hasPlottable(QCPAbstractPlottable *plottable) const
6635 {
6636 return mPlottables.contains(plottable);
6637 }
6638
6639 /*!
6640 Returns the graph with \a index. If the index is invalid, returns 0.
6641
6642 There is an overloaded version of this function with no parameter which returns the last created
6643 graph, see QCustomPlot::graph()
6644
6645 \see graphCount, addGraph
6646 */
6647 QCPGraph *QCustomPlot::graph(int index) const
6648 {
6649 if (index >= 0 && index < mGraphs.size())
6650 {
6651 return mGraphs.at(index);
6652 } else
6653 {
6654 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
6655 return 0;
6656 }
6657 }
6658
6659 /*! \overload
6660
6661 Returns the last graph, that was created with \ref addGraph. If there are no graphs in the plot,
6662 returns 0.
6663
6664 \see graphCount, addGraph
6665 */
6666 QCPGraph *QCustomPlot::graph() const
6667 {
6668 if (!mGraphs.isEmpty())
6669 {
6670 return mGraphs.last();
6671 } else
6672 return 0;
6673 }
6674
6675 /*!
6676 Creates a new graph inside the plot. If \a keyAxis and \a valueAxis are left unspecified (0), the
6677 bottom (xAxis) is used as key and the left (yAxis) is used as value. If specified, \a keyAxis and
6678 \a valueAxis must reside in this QCustomPlot.
6679
6680 \a keyAxis will be used as key axis (typically "x") and \a valueAxis as value axis (typically
6681 "y") for the graph.
6682
6683 Returns a pointer to the newly created graph.
6684
6685 \see graph, graphCount, removeGraph, clearGraphs
6686 */
6687 QCPGraph *QCustomPlot::addGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
6688 {
6689 if (!keyAxis) keyAxis = xAxis;
6690 if (!valueAxis) valueAxis = yAxis;
6691 if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this)
6692 {
6693 qDebug() << Q_FUNC_INFO << "passed keyAxis or valueAxis doesn't have this QCustomPlot as parent";
6694 return 0;
6695 }
6696
6697 QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis);
6698 if (addPlottable(newGraph))
6699 {
6700 newGraph->setName("Graph "+QString::number(mGraphs.size()));
6701 return newGraph;
6702 } else
6703 {
6704 delete newGraph;
6705 return 0;
6706 }
6707 }
6708
6709 /*!
6710 Removes the specified \a graph from the plot and, if necessary, from the legend. If
6711 any other graphs in the plot have a channel fill set towards the removed graph, the channel fill
6712 property of those graphs is reset to zero (no channel fill).
6713
6714 Returns true on success.
6715
6716 \see clearGraphs
6717 */
6718 bool QCustomPlot::removeGraph(QCPGraph *graph)
6719 {
6720 return removePlottable(graph);
6721 }
6722
6723 /*! \overload
6724
6725 Removes the graph by its \a index.
6726 */
6727 bool QCustomPlot::removeGraph(int index)
6728 {
6729 if (index >= 0 && index < mGraphs.size())
6730 return removeGraph(mGraphs[index]);
6731 else
6732 return false;
6733 }
6734
6735 /*!
6736 Removes all graphs from the plot (and the legend, if necessary).
6737 Returns the number of graphs removed.
6738 \see removeGraph
6739 */
6740 int QCustomPlot::clearGraphs()
6741 {
6742 int c = mGraphs.size();
6743 for (int i=c-1; i >= 0; --i)
6744 removeGraph(mGraphs[i]);
6745 return c;
6746 }
6747
6748 /*!
6749 Returns the number of currently existing graphs in the plot
6750
6751 \see graph, addGraph
6752 */
6753 int QCustomPlot::graphCount() const
6754 {
6755 return mGraphs.size();
6756 }
6757
6758 /*!
6759 Returns a list of the selected graphs. If no graphs are currently selected, the list is empty.
6760
6761 \note Even if the returned list is empty, it might still be, that there are selected plottables
6762 in the plot that are not of type QCPGraph (e.g. QCPCurve, QCPBars, etc.), see \ref
6763 selectedPlottables. Of course, this only applies, if you actually add non-QCPGraph plottables.
6764
6765 \see setInteractions, selectedPlottables, QCPAbstractPlottable::setSelectable, QCPAbstractPlottable::setSelected
6766 */
6767 QList<QCPGraph*> QCustomPlot::selectedGraphs() const
6768 {
6769 QList<QCPGraph*> result;
6770 for (int i=0; i<mGraphs.size(); ++i)
6771 {
6772 if (mGraphs.at(i)->selected())
6773 result.append(mGraphs.at(i));
6774 }
6775 return result;
6776 }
6777
6778 /*!
6779 Returns the item with \a index. If the index is invalid, returns 0.
6780
6781 There is an overloaded version of this function with no parameter which returns the last added
6782 item, see QCustomPlot::item()
6783
6784 \see itemCount, addItem
6785 */
6786 QCPAbstractItem *QCustomPlot::item(int index) const
6787 {
6788 if (index >= 0 && index < mItems.size())
6789 {
6790 return mItems.at(index);
6791 } else
6792 {
6793 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
6794 return 0;
6795 }
6796 }
6797
6798 /*! \overload
6799
6800 Returns the last item, that was added with \ref addItem. If there are no items in the plot,
6801 returns 0.
6802
6803 \see itemCount, addItem
6804 */
6805 QCPAbstractItem *QCustomPlot::item() const
6806 {
6807 if (!mItems.isEmpty())
6808 {
6809 return mItems.last();
6810 } else
6811 return 0;
6812 }
6813
6814 /*!
6815 Adds the specified item to the plot. QCustomPlot takes ownership of the item.
6816
6817 Returns true on success, i.e. when \a item wasn't already added to the plot and the parent plot
6818 of \a item is this QCustomPlot.
6819
6820 \see item, itemCount, removeItem, clearItems
6821 */
6822 bool QCustomPlot::addItem(QCPAbstractItem *item)
6823 {
6824 if (!mItems.contains(item) && item->parentPlot() == this)
6825 {
6826 mItems.append(item);
6827 return true;
6828 } else
6829 {
6830 qDebug() << Q_FUNC_INFO << "item either already in list or not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(item);
6831 return false;
6832 }
6833 }
6834
6835 /*!
6836 Removes the specified item from the plot.
6837
6838 Returns true on success.
6839
6840 \see addItem, clearItems
6841 */
6842 bool QCustomPlot::removeItem(QCPAbstractItem *item)
6843 {
6844 if (mItems.contains(item))
6845 {
6846 delete item;
6847 mItems.removeOne(item);
6848 return true;
6849 } else
6850 {
6851 qDebug() << Q_FUNC_INFO << "item not in list:" << reinterpret_cast<quintptr>(item);
6852 return false;
6853 }
6854 }
6855
6856 /*! \overload
6857
6858 Removes the item by its \a index.
6859 */
6860 bool QCustomPlot::removeItem(int index)
6861 {
6862 if (index >= 0 && index < mItems.size())
6863 return removeItem(mItems[index]);
6864 else
6865 {
6866 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
6867 return false;
6868 }
6869 }
6870
6871 /*!
6872 Removes all items from the plot.
6873
6874 Returns the number of items removed.
6875
6876 \see removeItem
6877 */
6878 int QCustomPlot::clearItems()
6879 {
6880 int c = mItems.size();
6881 for (int i=c-1; i >= 0; --i)
6882 removeItem(mItems[i]);
6883 return c;
6884 }
6885
6886 /*!
6887 Returns the number of currently existing items in the plot
6888
6889 \see item, addItem
6890 */
6891 int QCustomPlot::itemCount() const
6892 {
6893 return mItems.size();
6894 }
6895
6896 /*!
6897 Returns a list of the selected items. If no items are currently selected, the list is empty.
6898
6899 \see setInteractions, QCPAbstractItem::setSelectable, QCPAbstractItem::setSelected
6900 */
6901 QList<QCPAbstractItem*> QCustomPlot::selectedItems() const
6902 {
6903 QList<QCPAbstractItem*> result;
6904 for (int i=0; i<mItems.size(); ++i)
6905 {
6906 if (mItems.at(i)->selected())
6907 result.append(mItems.at(i));
6908 }
6909 return result;
6910 }
6911
6912 /*!
6913 Returns the item at the pixel position \a pos. Items that only consist of single lines (e.g. \ref
6914 QCPItemLine or \ref QCPItemCurve) have a tolerance band around them, see \ref
6915 setSelectionTolerance. If multiple items come into consideration, the one closest to \a pos is
6916 returned.
6917
6918 If \a onlySelectable is true, only items that are selectable (QCPAbstractItem::setSelectable) are
6919 considered.
6920
6921 If there is no item at \a pos, the return value is 0.
6922 */
6923 QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const
6924 {
6925 QCPAbstractItem *resultItem = 0;
6926 double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
6927
6928 for (int i=0; i<mItems.size(); ++i)
6929 {
6930 QCPAbstractItem *currentItem = mItems[i];
6931 if (onlySelectable && !currentItem->selectable())
6932 continue;
6933 if (!currentItem->clipToAxisRect() || currentItem->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it
6934 {
6935 double currentDistance = currentItem->selectTest(pos);
6936 if (currentDistance >= 0 && currentDistance < resultDistance)
6937 {
6938 resultItem = currentItem;
6939 resultDistance = currentDistance;
6940 }
6941 }
6942 }
6943
6944 return resultItem;
6945 }
6946
6947 /*!
6948 Returns the layer with the specified \a name.
6949
6950 \see addLayer, moveLayer, removeLayer
6951 */
6952 QCPLayer *QCustomPlot::layer(const QString &name) const
6953 {
6954 for (int i=0; i<mLayers.size(); ++i)
6955 {
6956 if (mLayers.at(i)->name() == name)
6957 return mLayers.at(i);
6958 }
6959 return 0;
6960 }
6961
6962 /*! \overload
6963
6964 Returns the layer by index.
6965
6966 \see addLayer, moveLayer, removeLayer
6967 */
6968 QCPLayer *QCustomPlot::layer(int index) const
6969 {
6970 if (index >= 0 && index < mLayers.size())
6971 {
6972 return mLayers.at(index);
6973 } else
6974 {
6975 qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
6976 return 0;
6977 }
6978 }
6979
6980 /*!
6981 Returns the layer that is set as current layer (see \ref setCurrentLayer).
6982 */
6983 QCPLayer *QCustomPlot::currentLayer() const
6984 {
6985 return mCurrentLayer;
6986 }
6987
6988 /*!
6989 Sets the layer with the specified \a name to be the current layer. All newly created/added
6990 layerables (\ref QCPLayerable), e.g. plottables and items, are initially placed on the current
6991 layer.
6992
6993 Returns true on success, i.e. if there is a layer with the specified \a name in the QCustomPlot.
6994
6995 \see addLayer, moveLayer, removeLayer
6996 */
6997 bool QCustomPlot::setCurrentLayer(const QString &name)
6998 {
6999 if (QCPLayer *newCurrentLayer = layer(name))
7000 {
7001 return setCurrentLayer(newCurrentLayer);
7002 } else
7003 {
7004 qDebug() << Q_FUNC_INFO << "layer with name doesn't exist:" << name;
7005 return false;
7006 }
7007 }
7008
7009 /*! \overload
7010
7011 Sets the provided \a layer to be the current layer.
7012
7013 Returns true on success, i.e. when \a layer is a valid layer in the QCustomPlot.
7014
7015 \see addLayer, moveLayer, removeLayer
7016 */
7017 bool QCustomPlot::setCurrentLayer(QCPLayer *layer)
7018 {
7019 if (!mLayers.contains(layer))
7020 {
7021 qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
7022 return false;
7023 }
7024
7025 mCurrentLayer = layer;
7026 return true;
7027 }
7028
7029 /*!
7030 Returns the number of currently existing layers in the plot
7031
7032 \see layer, addLayer
7033 */
7034 int QCustomPlot::layerCount() const
7035 {
7036 return mLayers.size();
7037 }
7038
7039 /*!
7040 Adds a new layer to this QCustomPlot instance. The new layer will have the name \a name, which must
7041 be unique. It is positioned either below or above \a otherLayer, which can be controlled with \a insertMode.
7042
7043 Returns true on success, i.e. if there is no other layer named \a name and \a otherLayer is a
7044 valid layer inside this QCustomPlot.
7045
7046 If \a otherLayer is 0, the highest layer in the QCustomPlot will be used.
7047
7048 For an explanation of what layers are in QCustomPlot, see the documentation of \ref QCPLayer.
7049
7050 \see layer, moveLayer, removeLayer
7051 */
7052 bool QCustomPlot::addLayer(const QString &name, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
7053 {
7054 if (!otherLayer)
7055 otherLayer = mLayers.last();
7056 if (!mLayers.contains(otherLayer))
7057 {
7058 qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
7059 return false;
7060 }
7061 if (layer(name))
7062 {
7063 qDebug() << Q_FUNC_INFO << "A layer exists already with the name" << name;
7064 return false;
7065 }
7066
7067 QCPLayer *newLayer = new QCPLayer(this, name);
7068 mLayers.insert(otherLayer->index() + (insertMode==limAbove ? 1:0), newLayer);
7069 return true;
7070 }
7071
7072 /*!
7073 Removes the specified \a layer and returns true on success.
7074
7075 All layerables (e.g. plottables and items) on the removed layer will be moved to the layer below
7076 \a layer. If \a layer is the bottom layer, the layerables are moved to the layer above. In both
7077 cases, the total rendering order of all layerables in the QCustomPlot is preserved.
7078
7079 If \a layer is the current layer (\ref setCurrentLayer), the layer below (or above, if bottom
7080 layer) becomes the new current layer.
7081
7082 Note that it is not possible to remove the last layer.
7083
7084 \see layer, addLayer, moveLayer
7085 */
7086 bool QCustomPlot::removeLayer(QCPLayer *layer)
7087 {
7088 if (!mLayers.contains(layer))
7089 {
7090 qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
7091 return false;
7092 }
7093 if (!mLayers.size() > 1)
7094 {
7095 qDebug() << Q_FUNC_INFO << "can't remove last layer";
7096 return false;
7097 }
7098
7099 // append all children of this layer to layer below (if this is lowest layer, prepend to layer above)
7100 int removedIndex = layer->index();
7101 bool isFirstLayer = removedIndex==0;
7102 QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex+1) : mLayers.at(removedIndex-1);
7103 QList<QCPLayerable*> children = layer->children();
7104 if (isFirstLayer) // prepend in reverse order (so order relative to each other stays the same)
7105 {
7106 for (int i=children.size()-1; i>=0; --i)
7107 children.at(i)->moveToLayer(targetLayer, true);
7108 } else // append normally
7109 {
7110 for (int i=0; i<children.size(); ++i)
7111 children.at(i)->moveToLayer(targetLayer, false);
7112 }
7113 // if removed layer is current layer, change current layer to layer below/above:
7114 if (layer == mCurrentLayer)
7115 setCurrentLayer(targetLayer);
7116 // remove layer:
7117 delete layer;
7118 mLayers.removeOne(layer);
7119 return true;
7120 }
7121
7122 /*!
7123 Moves the specified \a layer to the position relative to \a otherLayer. Whether \a layer is
7124 placed above or below \a otherLayer can be controlled with \a insertMode.
7125
7126 Returns true on success, i.e. when both \a layer and \a otherLayer are valid layers in the
7127 QCustomPlot.
7128
7129 \see layer, addLayer, moveLayer
7130 */
7131 bool QCustomPlot::moveLayer(QCPLayer *layer, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
7132 {
7133 if (!mLayers.contains(layer))
7134 {
7135 qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
7136 return false;
7137 }
7138 if (!mLayers.contains(otherLayer))
7139 {
7140 qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
7141 return false;
7142 }
7143
7144 mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 1:0));
7145 return true;
7146 }
7147
7148 /*!
7149 Returns the axes that currently have selected parts, i.e. whose selection is not \ref QCPAxis::spNone.
7150
7151 \see selectedPlottables, selectedLegends, setInteractions, QCPAxis::setSelected, QCPAxis::setSelectable
7152 */
7153 QList<QCPAxis*> QCustomPlot::selectedAxes() const
7154 {
7155 QList<QCPAxis*> result = QList<QCPAxis*>() << xAxis << yAxis << xAxis2 << yAxis2;
7156 for (int i=result.size()-1; i>=0; --i)
7157 {
7158 if (result.at(i)->selected() == QCPAxis::spNone)
7159 result.removeAt(i);
7160 }
7161 return result;
7162 }
7163
7164 /*!
7165 Returns the legends (typically one or zero) that currently have selected parts, i.e. whose
7166 selection is not \ref QCPLegend::spNone.
7167
7168 \see selectedPlottables, selectedAxes, setInteractions, QCPLegend::setSelected, QCPLegend::setSelectable, QCPLegend::selectedItems
7169 */
7170 QList<QCPLegend*> QCustomPlot::selectedLegends() const
7171 {
7172 /* for now, we only have the one legend. Maybe later, there will be a mechanism to have more. */
7173 QList<QCPLegend*> result;
7174 if (legend->selected() != QCPLegend::spNone)
7175 result.append(legend);
7176 return result;
7177 }
7178
7179 /*!
7180 Deselects everything in the QCustomPlot (plottables, items, axes, legend and title).
7181
7182 Since calling this function is not a user interaction, this does not emit the \ref
7183 selectionChangedByUser signal. The individual selectionChanged signals are emitted though, if the
7184 objects were previously selected.
7185
7186 \see setInteractions, selectedPlottables, selectedItems, selectedAxes, selectedLegends
7187 */
7188 void QCustomPlot::deselectAll()
7189 {
7190 // deselect plottables:
7191 QList<QCPAbstractPlottable*> selPlottables = selectedPlottables();
7192 for (int i=0; i<selPlottables.size(); ++i)
7193 selPlottables.at(i)->setSelected(false);
7194
7195 // deselect items:
7196 QList<QCPAbstractItem*> selItems = selectedItems();
7197 for (int i=0; i<selItems.size(); ++i)
7198 selItems.at(i)->setSelected(false);
7199
7200 // deselect axes:
7201 QList<QCPAxis*> selAxes = selectedAxes();
7202 for (int i=0; i<selAxes.size(); ++i)
7203 selAxes.at(i)->setSelected(QCPAxis::spNone);
7204
7205 // deselect legend (and legend items):
7206 legend->setSelected(QCPLegend::spNone);
7207
7208 // deselect title:
7209 setTitleSelected(false);
7210 }
7211
7212 /*!
7213 Causes a complete replot (axes, labels, graphs, etc.) into the internal buffer. Finally, update()
7214 is called, to redraw the buffer on the QCustomPlot widget surface.
7215
7216 Before the replot happens, the signal \ref beforeReplot is emitted. After the replot, \ref afterReplot is
7217 emitted. It is safe to mutually connect the replot slot with any of those two signals on two QCustomPlots
7218 to make them replot synchronously (i.e. it won't cause an infinite recursion).
7219 */
7220 void QCustomPlot::replot()
7221 {
7222 if (mReplotting) // incase signals loop back to replot slot
7223 return;
7224 mReplotting = true;
7225 emit beforeReplot();
7226 mPaintBuffer.fill(mColor);
7227 QCPPainter painter;
7228 painter.begin(&mPaintBuffer);
7229 if (painter.isActive())
7230 {
7231 painter.setRenderHint(QPainter::HighQualityAntialiasing);
7232 draw(&painter);
7233 if (mPlottingHints.testFlag(QCP::phForceRepaint))
7234 repaint();
7235 else
7236 update();
7237 painter.end();
7238 } else // might happen if QCustomPlot has width or height zero
7239 qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer";
7240 emit afterReplot();
7241 mReplotting = false;
7242 }
7243
7244 /*!
7245 Convenience function to make the top and right axes visible and assign them the following
7246 properties from their corresponding bottom/left axes:
7247
7248 \li range (\ref QCPAxis::setRange)
7249 \li range reversed (\ref QCPAxis::setRangeReversed)
7250 \li scale type (\ref QCPAxis::setScaleType)
7251 \li scale log base (\ref QCPAxis::setScaleLogBase)
7252 \li ticks (\ref QCPAxis::setTicks)
7253 \li auto (major) tick count (\ref QCPAxis::setAutoTickCount)
7254 \li sub tick count (\ref QCPAxis::setSubTickCount)
7255 \li auto sub ticks (\ref QCPAxis::setAutoSubTicks)
7256 \li tick step (\ref QCPAxis::setTickStep)
7257 \li auto tick step (\ref QCPAxis::setAutoTickStep)
7258
7259 Tick labels (\ref QCPAxis::setTickLabels) however, is always set to false.
7260
7261 This function does \a not connect the rangeChanged signals of the bottom and left axes to the \ref
7262 QCPAxis::setRange slots of the top and right axes in order to synchronize the ranges permanently.
7263 */
7264 void QCustomPlot::setupFullAxesBox()
7265 {
7266 xAxis2->setVisible(true);
7267 yAxis2->setVisible(true);
7268
7269 xAxis2->setTickLabels(false);
7270 yAxis2->setTickLabels(false);
7271
7272 xAxis2->setAutoSubTicks(xAxis->autoSubTicks());
7273 yAxis2->setAutoSubTicks(yAxis->autoSubTicks());
7274
7275 xAxis2->setAutoTickCount(xAxis->autoTickCount());
7276 yAxis2->setAutoTickCount(yAxis->autoTickCount());
7277
7278 xAxis2->setAutoTickStep(xAxis->autoTickStep());
7279 yAxis2->setAutoTickStep(yAxis->autoTickStep());
7280
7281 xAxis2->setScaleType(xAxis->scaleType());
7282 yAxis2->setScaleType(yAxis->scaleType());
7283
7284 xAxis2->setScaleLogBase(xAxis->scaleLogBase());
7285 yAxis2->setScaleLogBase(yAxis->scaleLogBase());
7286
7287 xAxis2->setTicks(xAxis->ticks());
7288 yAxis2->setTicks(yAxis->ticks());
7289
7290 xAxis2->setSubTickCount(xAxis->subTickCount());
7291 yAxis2->setSubTickCount(yAxis->subTickCount());
7292
7293 xAxis2->setTickStep(xAxis->tickStep());
7294 yAxis2->setTickStep(yAxis->tickStep());
7295
7296 xAxis2->setRange(xAxis->range());
7297 yAxis2->setRange(yAxis->range());
7298
7299 xAxis2->setRangeReversed(xAxis->rangeReversed());
7300 yAxis2->setRangeReversed(yAxis->rangeReversed());
7301 }
7302
7303 /*!
7304 Rescales the axes such that all plottables (e.g. graphs) in the plot are fully visible.
7305 It does this by calling \ref QCPAbstractPlottable::rescaleAxes on all plottables.
7306
7307 \see QCPAbstractPlottable::rescaleAxes
7308 */
7309 void QCustomPlot::rescaleAxes()
7310 {
7311 if (mPlottables.isEmpty()) return;
7312
7313 mPlottables.at(0)->rescaleAxes(false); // onlyEnlarge disabled on first plottable
7314 for (int i=1; i<mPlottables.size(); ++i)
7315 mPlottables.at(i)->rescaleAxes(true); // onlyEnlarge enabled on all other plottables
7316 }
7317
7318 /*!
7319 Saves a PDF with the vectorized plot to the file \a fileName. The axis ratio as well as the scale
7320 of texts and lines will be derived from the specified \a width and \a height. This means, the
7321 output will look like the normal on-screen output of a QCustomPlot widget with the corresponding
7322 pixel width and height. If either \a width or \a height is zero, the exported image will have
7323 the same dimensions as the QCustomPlot widget currently has.
7324
7325 \a noCosmeticPen disables the use of cosmetic pens when drawing to the PDF file. Cosmetic pens
7326 are pens with numerical width 0, which are always drawn as a one pixel wide line, no matter what
7327 zoom factor is set in the PDF-Viewer. For more information about cosmetic pens, see QPainter and
7328 QPen documentation.
7329
7330 The objects of the plot will appear in the current selection state. So when you don't want e.g.
7331 selected axes to be painted in their selected look, deselect everything with \ref deselectAll
7332 before calling this function.
7333
7334 Returns true on success.
7335
7336 \warning
7337 \li If you plan on editing the exported PDF file with a vector graphics editor like
7338 Inkscape, it is advised to set \a noCosmeticPen to true to avoid losing those cosmetic lines
7339 (which might be quite many, because cosmetic pens are the default for e.g. axes and tick marks).
7340 \li If calling this function inside the constructor of the parent of the QCustomPlot widget
7341 (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
7342 explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
7343 function uses the current width and height of the QCustomPlot widget. However, in Qt, these
7344 aren't defined yet inside the constructor, so you would get an image that has strange
7345 widths/heights.
7346
7347 \see savePng, saveBmp, saveJpg, saveRastered
7348 */
7349 bool QCustomPlot::savePdf(const QString &fileName, bool noCosmeticPen, int width, int height)
7350 {
7351 bool success = false;
7352 int newWidth, newHeight;
7353 if (width == 0 || height == 0)
7354 {
7355 newWidth = this->width();
7356 newHeight = this->height();
7357 } else
7358 {
7359 newWidth = width;
7360 newHeight = height;
7361 }
7362
7363 QPrinter printer(QPrinter::ScreenResolution);
7364 printer.setOutputFileName(fileName);
7365 printer.setFullPage(true);
7366 QRect oldViewport = mViewport;
7367 mViewport = QRect(0, 0, newWidth, newHeight);
7368 updateAxisRect();
7369 printer.setPaperSize(mViewport.size(), QPrinter::DevicePixel);
7370 QCPPainter printpainter;
7371 if (printpainter.begin(&printer))
7372 {
7373 printpainter.setPdfExportMode(true);
7374 printpainter.setWindow(mViewport);
7375 printpainter.setRenderHint(QPainter::NonCosmeticDefaultPen, noCosmeticPen);
7376 if (mColor != Qt::white && mColor != Qt::transparent && mColor.alpha() > 0) // draw pdf background color if not white/transparent
7377 printpainter.fillRect(mViewport, mColor);
7378 draw(&printpainter);
7379 printpainter.end();
7380 success = true;
7381 }
7382 mViewport = oldViewport;
7383 updateAxisRect();
7384 return success;
7385 }
7386
7387 /*!
7388 Saves a PNG image file to \a fileName on disc. The output plot will have the dimensions \a width
7389 and \a height in pixels. If either \a width or \a height is zero, the exported image will have
7390 the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not
7391 scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter.
7392
7393 For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an
7394 image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,
7395 texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full
7396 200*200 pixel resolution.
7397
7398 \warning If calling this function inside the constructor of the parent of the QCustomPlot widget
7399 (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
7400 explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
7401 function uses the current width and height of the QCustomPlot widget. However, in Qt, these
7402 aren't defined yet inside the constructor, so you would get an image that has strange
7403 widths/heights.
7404
7405 The objects of the plot will appear in the current selection state. If you don't want any selected
7406 objects to be painted in their selected look, deselect everything with \ref deselectAll before calling
7407 this function.
7408
7409 If you want the plot to be painted in a PNG with transparent background, call \ref setColor with a
7410 transparent color, e.g. Qt::transparent, before saving.
7411
7412 PNG compression can be controlled with the \a quality parameter which must be between 0 and 100 or
7413 -1 to use the default setting.
7414
7415 Returns true on success. If this function fails, most likely the PNG format isn't supported by
7416 the system, see Qt docs about QImageWriter::supportedImageFormats().
7417
7418 \see savePdf, saveBmp, saveJpg, saveRastered
7419 */
7420 bool QCustomPlot::savePng(const QString &fileName, int width, int height, double scale, int quality)
7421 {
7422 return saveRastered(fileName, width, height, scale, "PNG", quality);
7423 }
7424
7425 /*!
7426 Saves a JPG image file to \a fileName on disc. The output plot will have the dimensions \a width
7427 and \a height in pixels. If either \a width or \a height is zero, the exported image will have
7428 the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not
7429 scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter.
7430
7431 For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an
7432 image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,
7433 texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full
7434 200*200 pixel resolution.
7435
7436 \warning If calling this function inside the constructor of the parent of the QCustomPlot widget
7437 (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
7438 explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
7439 function uses the current width and height of the QCustomPlot widget. However, in Qt, these
7440 aren't defined yet inside the constructor, so you would get an image that has strange
7441 widths/heights.
7442
7443 The objects of the plot will appear in the current selection state. If you don't want any selected
7444 objects to be painted in their selected look, deselect everything with \ref deselectAll before calling
7445 this function.
7446
7447 JPG compression can be controlled with the \a quality parameter which must be between 0 and 100 or
7448 -1 to use the default setting.
7449
7450 Returns true on success. If this function fails, most likely the JPG format isn't supported by
7451 the system, see Qt docs about QImageWriter::supportedImageFormats().
7452
7453 \see savePdf, savePng, saveBmp, saveRastered
7454 */
7455 bool QCustomPlot::saveJpg(const QString &fileName, int width, int height, double scale, int quality)
7456 {
7457 return saveRastered(fileName, width, height, scale, "JPG", quality);
7458 }
7459
7460 /*!
7461 Saves a BMP image file to \a fileName on disc. The output plot will have the dimensions \a width
7462 and \a height in pixels. If either \a width or \a height is zero, the exported image will have
7463 the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not
7464 scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter.
7465
7466 For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an
7467 image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,
7468 texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full
7469 200*200 pixel resolution.
7470
7471 \warning If calling this function inside the constructor of the parent of the QCustomPlot widget
7472 (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
7473 explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
7474 function uses the current width and height of the QCustomPlot widget. However, in Qt, these
7475 aren't defined yet inside the constructor, so you would get an image that has strange
7476 widths/heights.
7477
7478 The objects of the plot will appear in the current selection state. If you don't want any selected
7479 objects to be painted in their selected look, deselect everything with \ref deselectAll before calling
7480 this function.
7481
7482 Returns true on success. If this function fails, most likely the BMP format isn't supported by
7483 the system, see Qt docs about QImageWriter::supportedImageFormats().
7484
7485 \see savePdf, savePng, saveJpg, saveRastered
7486 */
7487 bool QCustomPlot::saveBmp(const QString &fileName, int width, int height, double scale)
7488 {
7489 return saveRastered(fileName, width, height, scale, "BMP");
7490 }
7491
7492 /*! \internal
7493
7494 Returns a minimum size hint of QSize(50, 50). This prevents QCustomPlot from being collapsed to
7495 size/width zero when placed in a layout where other components try to take in as much space as
7496 possible (e.g. QMdiArea).
7497
7498 (To overwrite this minimum size hint of QCustomPlot, simply call QWidget::setMinimumSize in the
7499 QCustomPlot widget.)
7500 */
7501 QSize QCustomPlot::minimumSizeHint() const
7502 {
7503 return QSize(50, 50);
7504 }
7505
7506 /*! \internal
7507
7508 Event handler for when the QCustomPlot widget needs repainting. This does not cause a replot, but
7509 draws the internal buffer on the widget surface.
7510 */
7511 void QCustomPlot::paintEvent(QPaintEvent *event)
7512 {
7513 Q_UNUSED(event);
7514 QPainter painter(this);
7515 painter.drawPixmap(0, 0, mPaintBuffer);
7516 }
7517
7518 /*! \internal
7519
7520 Event handler for a resize of the QCustomPlot widget. Causes the internal buffer to be resized to
7521 the new size. The viewport and the axis rect are resized appropriately. Finally a replot is
7522 performed.
7523 */
7524 void QCustomPlot::resizeEvent(QResizeEvent *event)
7525 {
7526 // resize and repaint the buffer:
7527 mPaintBuffer = QPixmap(event->size());
7528 mViewport = rect();
7529 updateAxisRect();
7530 replot();
7531 }
7532
7533 /*! \internal
7534
7535 Event handler for when a double click occurs.
7536 */
7537 void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event)
7538 {
7539 emit mouseDoubleClick(event);
7540
7541 // emit specialized object double click signals:
7542 bool foundHit = false;
7543 // for legend:
7544 if (receivers(SIGNAL(legendDoubleClick(QCPLegend*,QCPAbstractLegendItem*,QMouseEvent*))) > 0)
7545 {
7546 if (legend->selectTestLegend(event->pos()))
7547 {
7548 emit legendDoubleClick(legend, legend->selectTestItem(event->pos()), event);
7549 foundHit = true;
7550 }
7551 }
7552 // for plottables:
7553 if (!foundHit && receivers(SIGNAL(plottableDoubleClick(QCPAbstractPlottable*,QMouseEvent*))) > 0)
7554 {
7555 if (QCPAbstractPlottable *ap = plottableAt(event->pos(), false))
7556 {
7557 emit plottableDoubleClick(ap, event);
7558 foundHit = true;
7559 }
7560 }
7561 // for items:
7562 if (!foundHit && receivers(SIGNAL(itemDoubleClick(QCPAbstractItem*,QMouseEvent*))) > 0)
7563 {
7564 if (QCPAbstractItem *ai = itemAt(event->pos(), false))
7565 {
7566 emit itemDoubleClick(ai, event);
7567 foundHit = true;
7568 }
7569 }
7570 // for axes:
7571 if (!foundHit && receivers(SIGNAL(axisDoubleClick(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*))) > 0)
7572 {
7573 QVector<QCPAxis*> axes = QVector<QCPAxis*>() << xAxis << yAxis << xAxis2 << yAxis2;
7574 for (int i=0; i<axes.size(); ++i)
7575 {
7576 QCPAxis::SelectablePart part = axes.at(i)->selectTest(event->pos());
7577 if (part != QCPAxis::spNone)
7578 {
7579 foundHit = true;
7580 emit axisDoubleClick(axes.at(i), part, event);
7581 break;
7582 }
7583 }
7584 }
7585 // for title:
7586 if (!foundHit && receivers(SIGNAL(titleDoubleClick(QMouseEvent*))) > 0)
7587 {
7588 if (selectTestTitle(event->pos()))
7589 {
7590 emit titleDoubleClick(event);
7591 foundHit = true;
7592 }
7593 }
7594 }
7595
7596 /*! \internal
7597
7598 Event handler for when a mouse button is pressed. If the left mouse button is pressed, the range
7599 dragging interaction is initialized (the actual range manipulation happens in the \ref
7600 mouseMoveEvent).
7601
7602 The mDragging flag is set to true and some anchor points are set that are needed to determine the
7603 distance the mouse was dragged in the mouse move/release events later.
7604
7605 \see mouseMoveEvent, mouseReleaseEvent
7606 */
7607 void QCustomPlot::mousePressEvent(QMouseEvent *event)
7608 {
7609 emit mousePress(event);
7610 mDragStart = event->pos(); // need this even when not LeftButton is pressed, to determine in releaseEvent whether it was a full click (no position change between press and release)
7611 if (event->buttons() & Qt::LeftButton)
7612 {
7613 mDragging = true;
7614 // initialize antialiasing backup in case we start dragging:
7615 if (mNoAntialiasingOnDrag)
7616 {
7617 mAADragBackup = antialiasedElements();
7618 mNotAADragBackup = notAntialiasedElements();
7619 }
7620 // Mouse range dragging interaction:
7621 if (mInteractions.testFlag(iRangeDrag))
7622 {
7623 mDragStartHorzRange = mRangeDragHorzAxis->range();
7624 mDragStartVertRange = mRangeDragVertAxis->range();
7625 }
7626 }
7627
7628 QWidget::mousePressEvent(event);
7629 }
7630
7631 /*! \internal
7632
7633 Event handler for when the cursor is moved. This is where the built-in range dragging mechanism
7634 is handled.
7635
7636 \see mousePressEvent, mouseReleaseEvent
7637 */
7638 void QCustomPlot::mouseMoveEvent(QMouseEvent *event)
7639 {
7640 emit mouseMove(event);
7641
7642 // Mouse range dragging interaction:
7643 if (mInteractions.testFlag(iRangeDrag))
7644 {
7645 if (mDragging)
7646 {
7647 if (mRangeDrag.testFlag(Qt::Horizontal))
7648 {
7649 if (mRangeDragHorzAxis->mScaleType == QCPAxis::stLinear)
7650 {
7651 double diff = mRangeDragHorzAxis->pixelToCoord(mDragStart.x()) - mRangeDragHorzAxis->pixelToCoord(event->pos().x());
7652 mRangeDragHorzAxis->setRange(mDragStartHorzRange.lower+diff, mDragStartHorzRange.upper+diff);
7653 } else if (mRangeDragHorzAxis->mScaleType == QCPAxis::stLogarithmic)
7654 {
7655 double diff = mRangeDragHorzAxis->pixelToCoord(mDragStart.x()) / mRangeDragHorzAxis->pixelToCoord(event->pos().x());
7656 mRangeDragHorzAxis->setRange(mDragStartHorzRange.lower*diff, mDragStartHorzRange.upper*diff);
7657 }
7658 }
7659 if (mRangeDrag.testFlag(Qt::Vertical))
7660 {
7661 if (mRangeDragVertAxis->mScaleType == QCPAxis::stLinear)
7662 {
7663 double diff = mRangeDragVertAxis->pixelToCoord(mDragStart.y()) - mRangeDragVertAxis->pixelToCoord(event->pos().y());
7664 mRangeDragVertAxis->setRange(mDragStartVertRange.lower+diff, mDragStartVertRange.upper+diff);
7665 } else if (mRangeDragVertAxis->mScaleType == QCPAxis::stLogarithmic)
7666 {
7667 double diff = mRangeDragVertAxis->pixelToCoord(mDragStart.y()) / mRangeDragVertAxis->pixelToCoord(event->pos().y());
7668 mRangeDragVertAxis->setRange(mDragStartVertRange.lower*diff, mDragStartVertRange.upper*diff);
7669 }
7670 }
7671 if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot
7672 {
7673 if (mNoAntialiasingOnDrag)
7674 setNotAntialiasedElements(QCP::aeAll);
7675 replot();
7676 }
7677 }
7678 }
7679
7680 QWidget::mouseMoveEvent(event);
7681 }
7682
7683 /*! \internal
7684
7685 Event handler for when a mouse button is released. This is where the selection mechanism is
7686 handled.
7687
7688 \see mousePressEvent, mouseMoveEvent
7689 */
7690 void QCustomPlot::mouseReleaseEvent(QMouseEvent *event)
7691 {
7692 emit mouseRelease(event);
7693 mDragging = false;
7694 bool doReplot = false;
7695 if (mNoAntialiasingOnDrag)
7696 {
7697 setAntialiasedElements(mAADragBackup);
7698 setNotAntialiasedElements(mNotAADragBackup);
7699 doReplot = true;
7700 }
7701
7702 // determine whether it was a drag or click operation:
7703 if ((mDragStart-event->pos()).manhattanLength() < 5) // was a click
7704 {
7705 // Mouse selection interaction:
7706 if ((mInteractions & (iSelectPlottables|iSelectItems|iSelectAxes|iSelectLegend|iSelectTitle)) > 0
7707 && event->button() == Qt::LeftButton)
7708 {
7709 bool selectionFound = false;
7710 bool emitChangedSignal = false;
7711 bool additiveSelection = mInteractions.testFlag(iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier);
7712 // Mouse selection of legend:
7713 if (mInteractions.testFlag(iSelectLegend))
7714 selectionFound |= legend->handleLegendSelection(event, additiveSelection, emitChangedSignal);
7715 // Mouse selection of plottables:
7716 if (mInteractions.testFlag(iSelectPlottables))
7717 selectionFound |= handlePlottableSelection((!selectionFound || additiveSelection) ? event : 0, additiveSelection, emitChangedSignal);
7718 // Mouse selection of items:
7719 if (mInteractions.testFlag(iSelectItems))
7720 selectionFound |= handleItemSelection((!selectionFound || additiveSelection) ? event : 0, additiveSelection, emitChangedSignal);
7721 // Mouse selection of axes:
7722 if (mInteractions.testFlag(iSelectAxes))
7723 selectionFound |= handleAxisSelection((!selectionFound || additiveSelection) ? event : 0, additiveSelection, emitChangedSignal);
7724 // Mouse selection of title:
7725 if (mInteractions.testFlag(iSelectTitle))
7726 selectionFound |= handleTitleSelection((!selectionFound || additiveSelection) ? event : 0, additiveSelection, emitChangedSignal);
7727
7728 if (emitChangedSignal)
7729 emit selectionChangedByUser();
7730 doReplot = true;
7731 }
7732
7733 // emit specialized object click signals:
7734 bool foundHit = false;
7735 // for legend:
7736 if (receivers(SIGNAL(legendClick(QCPLegend*,QCPAbstractLegendItem*,QMouseEvent*))) > 0)
7737 {
7738 if (legend->selectTestLegend(event->pos()))
7739 {
7740 emit legendClick(legend, legend->selectTestItem(event->pos()), event);
7741 foundHit = true;
7742 }
7743 }
7744 // for plottables:
7745 if (!foundHit && receivers(SIGNAL(plottableClick(QCPAbstractPlottable*,QMouseEvent*))) > 0)
7746 {
7747 if (QCPAbstractPlottable *ap = plottableAt(event->pos(), false))
7748 {
7749 emit plottableClick(ap, event);
7750 foundHit = true;
7751 }
7752 }
7753 // for items:
7754 if (!foundHit && receivers(SIGNAL(itemClick(QCPAbstractItem*,QMouseEvent*))) > 0)
7755 {
7756 if (QCPAbstractItem *ai = itemAt(event->pos(), false))
7757 {
7758 emit itemClick(ai, event);
7759 foundHit = true;
7760 }
7761 }
7762 // for axes:
7763 if (!foundHit && receivers(SIGNAL(axisClick(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*))) > 0)
7764 {
7765 QVector<QCPAxis*> axes = QVector<QCPAxis*>() << xAxis << yAxis << xAxis2 << yAxis2;
7766 for (int i=0; i<axes.size(); ++i)
7767 {
7768 QCPAxis::SelectablePart part = axes.at(i)->selectTest(event->pos());
7769 if (part != QCPAxis::spNone)
7770 {
7771 foundHit = true;
7772 emit axisClick(axes.at(i), part, event);
7773 break;
7774 }
7775 }
7776 }
7777 // for title:
7778 if (!foundHit && receivers(SIGNAL(titleClick(QMouseEvent*))) > 0)
7779 {
7780 if (selectTestTitle(event->pos()))
7781 {
7782 emit titleClick(event);
7783 foundHit = true;
7784 }
7785 }
7786 } // was a click end
7787
7788 if (doReplot)
7789 replot();
7790
7791 QWidget::mouseReleaseEvent(event);
7792 }
7793
7794 /*! \internal
7795
7796 Event handler for mouse wheel events. First, the mouseWheel signal is emitted.
7797 If rangeZoom is Qt::Horizontal, Qt::Vertical or both, the ranges of the axes defined as
7798 rangeZoomHorzAxis and rangeZoomVertAxis are scaled. The center of the scaling
7799 operation is the current cursor position inside the plot. The scaling factor
7800 is dependant on the mouse wheel delta (which direction the wheel was rotated)
7801 to provide a natural zooming feel. The Strength of the zoom can be controlled via
7802 \ref setRangeZoomFactor.
7803
7804 Note, that event->delta() is usually +/-120 for single rotation steps. However, if the mouse
7805 wheel is turned rapidly, many steps may bunch up to one event, so the event->delta() may then be
7806 multiples of 120. This is taken into account here, by calculating \a wheelSteps and using it as
7807 exponent of the range zoom factor. This takes care of the wheel direction automatically, by
7808 inverting the factor, when the wheel step is negative (f^-1 = 1/f).
7809 */
7810 void QCustomPlot::wheelEvent(QWheelEvent *event)
7811 {
7812 emit mouseWheel(event);
7813
7814 // Mouse range zooming interaction:
7815 if (mInteractions.testFlag(iRangeZoom))
7816 {
7817 if (mRangeZoom != 0)
7818 {
7819 double factor;
7820 double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
7821 if (mRangeZoom.testFlag(Qt::Horizontal))
7822 {
7823 factor = pow(mRangeZoomFactorHorz, wheelSteps);
7824 mRangeZoomHorzAxis->scaleRange(factor, mRangeZoomHorzAxis->pixelToCoord(event->pos().x()));
7825 }
7826 if (mRangeZoom.testFlag(Qt::Vertical))
7827 {
7828 factor = pow(mRangeZoomFactorVert, wheelSteps);
7829 mRangeZoomVertAxis->scaleRange(factor, mRangeZoomVertAxis->pixelToCoord(event->pos().y()));
7830 }
7831 replot();
7832 }
7833 }
7834
7835 QWidget::wheelEvent(event);
7836 }
7837
7838 /*! \internal
7839
7840 Handles a mouse \a event for the plottable selection interaction. Returns true, when a selectable
7841 plottable was hit by the mouse event. The output variable \a modified is set to true when the
7842 selection state of a plottable has changed.
7843
7844 When \a additiveSelecton is true, any new selections become selected in addition to the recent
7845 selections. The recent selections are not cleared. Further, clicking on one object multiple times
7846 in additive selection mode, toggles the selection of that object on and off.
7847
7848 To indicate that all plottables that are selectable shall be deselected, pass 0 as \a event.
7849
7850 Unlike for axis and legend selection, this function can't be exported to the respective class
7851 itself (i.e. QCPAbstractPlottable). The function needs to know the distance of the mouse event to
7852 all plottables in the plot, in order to choose the plottable with the smallest distance. This
7853 wouldn't work if it were local to a single plottable.
7854 */
7855 bool QCustomPlot::handlePlottableSelection(QMouseEvent *event, bool additiveSelection, bool &modified)
7856 {
7857 // Note: This code is basically identical to handleItemSelection, only for plottables
7858
7859 bool selectionFound = false;
7860 if (event)
7861 {
7862 QCPAbstractPlottable *plottableSelection = plottableAt(event->pos(), true);
7863 // handle selection of found plottable:
7864 if (plottableSelection)
7865 {
7866 selectionFound = true;
7867 if (!plottableSelection->selected() || additiveSelection)
7868 {
7869 plottableSelection->setSelected(!plottableSelection->selected());
7870 modified = true;
7871 }
7872 }
7873 // deselect all others (if plottableSelection is 0, all plottables are deselected):
7874 if (!additiveSelection)
7875 {
7876 for (int i=0; i<mPlottables.size(); ++i)
7877 {
7878 if (mPlottables.at(i) != plottableSelection && mPlottables.at(i)->selected() && mPlottables.at(i)->selectable())
7879 {
7880 mPlottables.at(i)->setSelected(false);
7881 modified = true;
7882 }
7883 }
7884 }
7885 } else // event == 0, so deselect selectable plottables
7886 {
7887 for (int i=0; i<mPlottables.size(); ++i)
7888 {
7889 if (mPlottables.at(i)->selected() && mPlottables.at(i)->selectable())
7890 {
7891 mPlottables.at(i)->setSelected(false);
7892 modified = true;
7893 }
7894 }
7895 }
7896 return selectionFound;
7897 }
7898
7899 /*! \internal
7900
7901 Handles a mouse \a event for the item selection interaction. Returns true, when a selectable
7902 item was hit by the mouse event. The output variable \a modified is set to true when the
7903 selection state of an item has changed.
7904
7905 When \a additiveSelecton is true, any new selections become selected in addition to the recent
7906 selections. The recent selections are not cleared. Further, clicking on one object multiple times
7907 in additive selection mode, toggles the selection of that object on and off.
7908
7909 To indicate that all items that are selectable shall be deselected, pass 0 as \a event.
7910
7911 Unlike for axis and legend selection, this function can't be exported to the respective class
7912 itself (i.e. QCPAbstractItem). The function needs to know the distance of the mouse event to
7913 all items in the plot, in order to choose the item with the smallest distance. This
7914 wouldn't work if it were local to a single item.
7915 */
7916 bool QCustomPlot::handleItemSelection(QMouseEvent *event, bool additiveSelection, bool &modified)
7917 {
7918 // Note: This code is basically identical to handlePlottableSelection, only for items
7919
7920 bool selectionFound = false;
7921 if (event)
7922 {
7923 QCPAbstractItem *itemSelection = itemAt(event->pos(), true);
7924 // handle selection of found plottable:
7925 if (itemSelection)
7926 {
7927 selectionFound = true;
7928 if (!itemSelection->selected() || additiveSelection)
7929 {
7930 itemSelection->setSelected(!itemSelection->selected());
7931 modified = true;
7932 }
7933 }
7934 // deselect all others (if itemSelection is 0, all items are deselected):
7935 if (!additiveSelection)
7936 {
7937 for (int i=0; i<mItems.size(); ++i)
7938 {
7939 if (mItems.at(i) != itemSelection && mItems.at(i)->selected() && mItems.at(i)->selectable())
7940 {
7941 mItems.at(i)->setSelected(false);
7942 modified = true;
7943 }
7944 }
7945 }
7946 } else // event == 0, so deselect selectable items
7947 {
7948 for (int i=0; i<mItems.size(); ++i)
7949 {
7950 if (mItems.at(i)->selected() && mItems.at(i)->selectable())
7951 {
7952 mItems.at(i)->setSelected(false);
7953 modified = true;
7954 }
7955 }
7956 }
7957 return selectionFound;
7958 }
7959
7960 /*! \internal
7961
7962 Handles a mouse \a event for the axis selection interaction. Returns true, when a selectable axis
7963 part was hit by the mouse event. The output variable \a modified is set to true when the
7964 selection state of an axis has changed.
7965
7966 When \a additiveSelecton is true, any new selections become selected in addition to the recent
7967 selections. The recent selections are not cleared. Further, clicking on one object multiple times
7968 in additive selection mode, toggles the selection of that object on and off.
7969
7970 To indicate that all axes shall be deselected, pass 0 as \a event.
7971 */
7972 bool QCustomPlot::handleAxisSelection(QMouseEvent *event, bool additiveSelection, bool &modified)
7973 {
7974 bool selectionFound = false;
7975 QVector<QCPAxis*> axes = QVector<QCPAxis*>() << xAxis << yAxis << xAxis2 << yAxis2;
7976 for (int i=0; i<axes.size(); ++i)
7977 selectionFound |= axes.at(i)->handleAxisSelection((!selectionFound || additiveSelection) ? event : 0, additiveSelection, modified);
7978 return selectionFound;
7979 }
7980
7981 /*! \internal
7982
7983 Handles a mouse \a event for the title selection interaction. Returns true, when the title was
7984 hit by the mouse event. The output variable \a modified is set to true when the selection state
7985 of the title has changed.
7986
7987 When \a additiveSelecton is true, any new selections become selected in addition to the recent
7988 selections. The recent selections are not cleared. Further, clicking on one object multiple times
7989 in additive selection mode, toggles the selection of that object on and off.
7990
7991 To indicate that the title shall be deselected, pass 0 as \a event.
7992 */
7993 bool QCustomPlot::handleTitleSelection(QMouseEvent *event, bool additiveSelection, bool &modified)
7994 {
7995 bool selectionFound = false;
7996 if (event && selectTestTitle(event->pos())) // hit, select title
7997 {
7998 selectionFound = true;
7999 if (!titleSelected() || additiveSelection)
8000 {
8001 setTitleSelected(!titleSelected());
8002 modified = true;
8003 }
8004 } else // no hit or event == 0, deselect title
8005 {
8006 if (titleSelected() && !additiveSelection)
8007 {
8008 setTitleSelected(false);
8009 modified = true;
8010 }
8011 }
8012 return selectionFound;
8013 }
8014
8015 /*! \internal
8016
8017 This is the main draw function which first generates the tick vectors of all axes,
8018 calculates and applies appropriate margins if autoMargin is true and finally draws
8019 all elements with the passed \a painter. (axis background, title, subgrid, grid, axes, plottables)
8020 */
8021 void QCustomPlot::draw(QCPPainter *painter)
8022 {
8023 // calculate title bounding box:
8024 if (!mTitle.isEmpty())
8025 {
8026 painter->setFont(titleSelected() ? mSelectedTitleFont : mTitleFont);
8027 mTitleBoundingBox = painter->fontMetrics().boundingRect(mViewport, Qt::TextDontClip | Qt::AlignHCenter, mTitle);
8028 } else
8029 mTitleBoundingBox = QRect();
8030
8031 // prepare values of ticks and tick strings:
8032 xAxis->setupTickVectors();
8033 yAxis->setupTickVectors();
8034 xAxis2->setupTickVectors();
8035 yAxis2->setupTickVectors();
8036 // set auto margin such that tick/axis labels etc. are not clipped:
8037 if (mAutoMargin)
8038 {
8039 setMargin(yAxis->calculateMargin(),
8040 yAxis2->calculateMargin(),
8041 xAxis2->calculateMargin()+mTitleBoundingBox.height(),
8042 xAxis->calculateMargin());
8043 }
8044 // position legend:
8045 legend->reArrange();
8046
8047 // draw axis background:
8048 drawAxisBackground(painter);
8049
8050 // draw all layered objects (grid, axes, plottables, items, legend,...):
8051 for (int layerIndex=0; layerIndex < mLayers.size(); ++layerIndex)
8052 {
8053 QList<QCPLayerable*> layerChildren = mLayers.at(layerIndex)->children();
8054 for (int k=0; k < layerChildren.size(); ++k)
8055 {
8056 QCPLayerable *child = layerChildren.at(k);
8057 if (child->visible())
8058 {
8059 painter->save();
8060 painter->setClipRect(child->clipRect().translated(0, -1));
8061 child->applyDefaultAntialiasingHint(painter);
8062 child->draw(painter);
8063 painter->restore();
8064 }
8065 }
8066 }
8067
8068 // draw title:
8069 if (!mTitle.isEmpty())
8070 {
8071 painter->setFont(titleSelected() ? mSelectedTitleFont : mTitleFont);
8072 painter->setPen(QPen(titleSelected() ? mSelectedTitleColor : mTitleColor));
8073 painter->drawText(mTitleBoundingBox, Qt::TextDontClip | Qt::AlignHCenter, mTitle);
8074 }
8075 }
8076
8077 /*! \internal
8078
8079 If an axis background is provided via \ref setAxisBackground, this function first buffers the
8080 scaled version depending on \ref setAxisBackgroundScaled and \ref setAxisBackgroundScaledMode and
8081 then draws it inside the current axisRect with the provided \a painter. The scaled version is
8082 buffered in mScaledAxisBackground to prevent the need for rescaling at every redraw. It is only
8083 updated, when the axisRect has changed in a way that requires a rescale of the background pixmap
8084 (this is dependant on the \ref setAxisBackgroundScaledMode), or when a differend axis backgroud
8085 was set.
8086
8087 \see draw, setAxisBackground, setAxisBackgroundScaled, setAxisBackgroundScaledMode
8088 */
8089 void QCustomPlot::drawAxisBackground(QCPPainter *painter)
8090 {
8091 if (!mAxisBackground.isNull())
8092 {
8093 if (mAxisBackgroundScaled)
8094 {
8095 // check whether mScaledAxisBackground needs to be updated:
8096 QSize scaledSize(mAxisBackground.size());
8097 scaledSize.scale(mAxisRect.size(), mAxisBackgroundScaledMode);
8098 if (mScaledAxisBackground.size() != scaledSize)
8099 mScaledAxisBackground = mAxisBackground.scaled(mAxisRect.size(), mAxisBackgroundScaledMode, Qt::SmoothTransformation);
8100 painter->drawPixmap(mAxisRect.topLeft(), mScaledAxisBackground, QRect(0, 0, mAxisRect.width(), mAxisRect.height()) & mScaledAxisBackground.rect());
8101 } else
8102 {
8103 painter->drawPixmap(mAxisRect.topLeft(), mAxisBackground, QRect(0, 0, mAxisRect.width(), mAxisRect.height()));
8104 }
8105 }
8106 }
8107
8108 /*! \internal
8109
8110 calculates mAxisRect by applying the margins inward to mViewport. The axisRect is then passed on
8111 to all axes via QCPAxis::setAxisRect
8112
8113 \see setMargin, setAxisRect
8114 */
8115 void QCustomPlot::updateAxisRect()
8116 {
8117 mAxisRect = mViewport.adjusted(mMarginLeft, mMarginTop, -mMarginRight, -mMarginBottom);
8118 xAxis->setAxisRect(mAxisRect);
8119 yAxis->setAxisRect(mAxisRect);
8120 xAxis2->setAxisRect(mAxisRect);
8121 yAxis2->setAxisRect(mAxisRect);
8122 }
8123
8124 /*! \internal
8125
8126 Returns whether the point \a pos in pixels hits the plot title.
8127 */
8128 bool QCustomPlot::selectTestTitle(const QPointF &pos) const
8129 {
8130 return mTitleBoundingBox.contains(pos.toPoint());
8131 }
8132
8133 /*!
8134 Saves the plot to a rastered image file \a fileName in the image format \a
8135 format. The plot is sized to \a width and \a height in pixels and scaled with
8136 \a scale. (width 100 and scale 2.0 lead to a full resolution file with width
8137 200) If the \a format supports compression, \a quality may be between 0 and
8138 100 to control it.
8139
8140 Returns true on success. If this function fails, most likely the given \a format isn't supported
8141 by the system, see Qt docs about QImageWriter::supportedImageFormats().
8142
8143 \see saveBmp, saveJpg, savePng
8144 */
8145 bool QCustomPlot::saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality)
8146 {
8147 int newWidth, newHeight;
8148 if (width == 0 || height == 0)
8149 {
8150 newWidth = this->width();
8151 newHeight = this->height();
8152 } else
8153 {
8154 newWidth = width;
8155 newHeight = height;
8156 }
8157 int scaledWidth = qRound(scale*newWidth);
8158 int scaledHeight = qRound(scale*newHeight);
8159
8160 QPixmap pngBuffer(scaledWidth, scaledHeight); // use QPixmap instead of QImage (like live painting buffer), because it supports background transparency (of mColor).
8161 pngBuffer.fill(mColor);
8162 QCPPainter painter(&pngBuffer);
8163 QRect oldViewport = mViewport;
8164 mViewport = QRect(0, 0, newWidth, newHeight);
8165 updateAxisRect();
8166 if (!qFuzzyCompare(scale, 1.0))
8167 {
8168 if (scale > 1.0) // for scale < 1 we always want cosmetic pens where possible, because else lines would disappear
8169 {
8170 painter.setScaledExportMode(true);
8171 painter.setRenderHint(QPainter::NonCosmeticDefaultPen);
8172 }
8173 painter.scale(scale, scale);
8174 }
8175 draw(&painter);
8176 mViewport = oldViewport;
8177 updateAxisRect();
8178 return pngBuffer.save(fileName, format, quality);
8179 }
8180
8181
8182 // ================================================================================
8183 // =================== QCPAbstractPlottable
8184 // ================================================================================
8185
8186 /*! \class QCPAbstractPlottable
8187 \brief The abstract base class for all data representing objects in a plot.
8188
8189 It defines a very basic interface like name, pen, brush, visibility etc. Since this class is
8190 abstract, it can't be instantiated. Use one of the subclasses or create a subclass yourself (see
8191 below), to create new ways of displaying data.
8192
8193 All further specifics are in the subclasses, for example:
8194 \li A normal graph with possibly a line, scatter points and error bars is displayed by \ref QCPGraph
8195 (typically created with \ref QCustomPlot::addGraph).
8196 \li A parametric curve can be displayed with \ref QCPCurve.
8197 \li A stackable bar chart can be achieved with \ref QCPBars.
8198 \li A box of a statistical box plot is created with \ref QCPStatisticalBox.
8199
8200 \section plottables-subclassing Creating own plottables
8201
8202 To create an own plottable, you implement a subclass of QCPAbstractPlottable. These are the pure
8203 virtual functions, you must implement:
8204 \li \ref clearData
8205 \li \ref selectTest
8206 \li \ref draw
8207 \li \ref drawLegendIcon
8208 \li \ref getKeyRange
8209 \li \ref getValueRange
8210
8211 See the documentation of those functions for what they need to do.
8212
8213 For drawing your plot, you can use the \ref coordsToPixels functions to translate a point in plot
8214 coordinates to pixel coordinates. This function is quite convenient, because it takes the
8215 orientation of the key and value axes into account for you (x and y are swapped when the key axis
8216 is vertical and the value axis horizontal). If you are worried about performance (i.e. you need
8217 to translate many points in a loop like QCPGraph), you can directly use \ref
8218 QCPAxis::coordToPixel. However, you must then take care about the orientation of the axis
8219 yourself.
8220
8221 From QCPAbstractPlottable you inherit the following members you may use:
8222 <table>
8223 <tr>
8224 <td>QCustomPlot *\b mParentPlot</td>
8225 <td>A pointer to the parent QCustomPlot instance. This is adopted from the axes that are passed in the constructor.</td>
8226 </tr><tr>
8227 <td>QString \b mName</td>
8228 <td>The name of the plottable.</td>
8229 </tr><tr>
8230 <td>bool \b mVisible</td>
8231 <td>Whether the plot is visible or not. When this is false, you shouldn't draw the data in the \ref draw function (\ref draw is always called, no matter what mVisible is).</td>
8232 </tr><tr>
8233 <td>QPen \b mPen</td>
8234 <td>The generic pen of the plottable. You should use this pen for the most prominent data representing lines in the plottable (e.g QCPGraph uses this pen for its graph lines and scatters)</td>
8235 </tr><tr>
8236 <td>QPen \b mSelectedPen</td>
8237 <td>The generic pen that should be used when the plottable is selected (hint: \ref mainPen gives you the right pen, depending on selection state).</td>
8238 </tr><tr>
8239 <td>QBrush \b mBrush</td>
8240 <td>The generic brush of the plottable. You should use this brush for the most prominent fillable structures in the plottable (e.g. QCPGraph uses this brush to control filling under the graph)</td>
8241 </tr><tr>
8242 <td>QBrush \b mSelectedBrush</td>
8243 <td>The generic brush that should be used when the plottable is selected (hint: \ref mainBrush gives you the right brush, depending on selection state).</td>
8244 </tr><tr>
8245 <td>QCPAxis *\b mKeyAxis, *\b mValueAxis</td>
8246 <td>The key and value axes this plottable is attached to. Call their QCPAxis::coordToPixel functions to translate coordinates to pixels in either the key or value dimension.</td>
8247 </tr><tr>
8248 <td>bool \b mSelected</td>
8249 <td>indicates whether the plottable is selected or not.</td>
8250 </tr>
8251 </table>
8252 */
8253
8254 /* start of documentation of pure virtual functions */
8255
8256 /*! \fn void QCPAbstractPlottable::clearData() = 0
8257 Clears all data in the plottable.
8258 */
8259
8260 /*! \fn double QCPAbstractPlottable::selectTest(const QPointF &pos) const = 0
8261
8262 This function is used to decide whether a click hits a plottable or not.
8263
8264 \a pos is a point in pixel coordinates on the QCustomPlot surface. This function returns the
8265 shortest pixel distance of this point to the plottable (e.g. to the scatters/lines of a graph).
8266 If the plottable is either invisible, contains no data or the distance couldn't be determined,
8267 -1.0 is returned. \ref setSelectable has no influence on the return value of this function.
8268
8269 If the plottable is represented not by single lines but by an area like QCPBars or
8270 QCPStatisticalBox, a click inside the area returns a constant value greater zero (typically 99%
8271 of the selectionTolerance of the parent QCustomPlot). If the click lies outside the area, this
8272 function returns -1.0.
8273
8274 Providing a constant value for area objects allows selecting line objects even when they are
8275 obscured by such area objects, by clicking close to the lines (i.e. closer than
8276 0.99*selectionTolerance).
8277
8278 The actual setting of the selection state is not done by this function. This is handled by the
8279 parent QCustomPlot when the mouseReleaseEvent occurs.
8280
8281 \see setSelected, QCustomPlot::setInteractions
8282 */
8283
8284 /*! \fn void QCPAbstractPlottable::draw(QCPPainter *painter) = 0
8285 \internal
8286
8287 Draws this plottable with the provided \a painter. Called by \ref QCustomPlot::draw on all its
8288 visible plottables.
8289
8290 The cliprect of the provided painter is set to the axis rect of the key/value axis of this
8291 plottable (what \ref clipRect returns), before this function is called.
8292 */
8293
8294 /*! \fn void QCPAbstractPlottable::drawLegendIcon(QCPPainter *painter, const QRect &rect) const = 0
8295 \internal
8296
8297 called by QCPLegend::draw (via QCPPlottableLegendItem::draw) to create a graphical representation
8298 of this plottable inside \a rect, next to the plottable name.
8299 */
8300
8301 /*! \fn QCPRange QCPAbstractPlottable::getKeyRange(bool &validRange, SignDomain inSignDomain) const = 0
8302 \internal
8303
8304 called by rescaleAxes functions to get the full data key bounds. For logarithmic plots, one can
8305 set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
8306 returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
8307 to \ref sdNegative and all positive points will be ignored for range calculation. For no
8308 restriction, just set \a inSignDomain to \ref sdBoth (default). \a validRange is an output
8309 parameter that indicates whether a proper range could be found or not. If this is false, you
8310 shouldn't use the returned range (e.g. no points in data).
8311
8312 \see rescaleAxes, getValueRange
8313 */
8314
8315 /*! \fn QCPRange QCPAbstractPlottable::getValueRange(bool &validRange, SignDomain inSignDomain) const = 0
8316 \internal
8317
8318 called by rescaleAxes functions to get the full data value bounds. For logarithmic plots, one can
8319 set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
8320 returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
8321 to \ref sdNegative and all positive points will be ignored for range calculation. For no
8322 restriction, just set \a inSignDomain to \ref sdBoth (default). \a validRange is an output
8323 parameter that indicates whether a proper range could be found or not. If this is false, you
8324 shouldn't use the returned range (e.g. no points in data).
8325
8326 \see rescaleAxes, getKeyRange
8327 */
8328
8329 /* end of documentation of pure virtual functions */
8330 /* start of documentation of signals */
8331
8332 /*! \fn void QCPAbstractPlottable::selectionChanged(bool selected)
8333 This signal is emitted when the selection state of this plottable has changed, either by user interaction
8334 or by a direct call to \ref setSelected.
8335 */
8336
8337 /* end of documentation of signals */
8338
8339 /*!
8340 Constructs an abstract plottable which uses \a keyAxis as its key axis ("x") and \a valueAxis as
8341 its value axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance
8342 and not have the same orientation. If either of these restrictions is violated, a corresponding
8343 message is printed to the debug output (qDebug), the construction is not aborted, though.
8344
8345 Since QCPAbstractPlottable is an abstract class that defines the basic interface to plottables
8346 (i.e. any form of data representation inside a plot, like graphs, curves etc.), it can't be
8347 directly instantiated.
8348
8349 You probably want one of the subclasses like \ref QCPGraph and \ref QCPCurve instead.
8350 \see setKeyAxis, setValueAxis
8351 */
8352 QCPAbstractPlottable::QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis) :
8353 QCPLayerable(keyAxis->parentPlot()),
8354 mName(""),
8355 mAntialiasedFill(true),
8356 mAntialiasedScatters(true),
8357 mAntialiasedErrorBars(false),
8358 mPen(Qt::black),
8359 mSelectedPen(Qt::black),
8360 mBrush(Qt::NoBrush),
8361 mSelectedBrush(Qt::NoBrush),
8362 mKeyAxis(keyAxis),
8363 mValueAxis(valueAxis),
8364 mSelected(false),
8365 mSelectable(true)
8366 {
8367 if (keyAxis->parentPlot() != valueAxis->parentPlot())
8368 qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis.";
8369 if (keyAxis->orientation() == valueAxis->orientation())
8370 qDebug() << Q_FUNC_INFO << "keyAxis and valueAxis must be orthogonal to each other.";
8371 }
8372
8373 /*!
8374 The name is the textual representation of this plottable as it is displayed in the QCPLegend of
8375 the parent QCustomPlot. It may contain any utf-8 characters, including newlines.
8376 */
8377 void QCPAbstractPlottable::setName(const QString &name)
8378 {
8379 mName = name;
8380 }
8381
8382 /*!
8383 Sets whether fills of this plottable is drawn antialiased or not.
8384
8385 Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
8386 QCustomPlot::setNotAntialiasedElements.
8387 */
8388 void QCPAbstractPlottable::setAntialiasedFill(bool enabled)
8389 {
8390 mAntialiasedFill = enabled;
8391 }
8392
8393 /*!
8394 Sets whether the scatter symbols of this plottable are drawn antialiased or not.
8395
8396 Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
8397 QCustomPlot::setNotAntialiasedElements.
8398 */
8399 void QCPAbstractPlottable::setAntialiasedScatters(bool enabled)
8400 {
8401 mAntialiasedScatters = enabled;
8402 }
8403
8404 /*!
8405 Sets whether the error bars of this plottable are drawn antialiased or not.
8406
8407 Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
8408 QCustomPlot::setNotAntialiasedElements.
8409 */
8410 void QCPAbstractPlottable::setAntialiasedErrorBars(bool enabled)
8411 {
8412 mAntialiasedErrorBars = enabled;
8413 }
8414
8415
8416 /*!
8417 The pen is used to draw basic lines that make up the plottable representation in the
8418 plot.
8419
8420 For example, the \ref QCPGraph subclass draws its graph lines and scatter points
8421 with this pen.
8422
8423 \see setBrush
8424 */
8425 void QCPAbstractPlottable::setPen(const QPen &pen)
8426 {
8427 mPen = pen;
8428 }
8429
8430 /*!
8431 When the plottable is selected, this pen is used to draw basic lines instead of the normal
8432 pen set via \ref setPen.
8433
8434 \see setSelected, setSelectable, setSelectedBrush, selectTest
8435 */
8436 void QCPAbstractPlottable::setSelectedPen(const QPen &pen)
8437 {
8438 mSelectedPen = pen;
8439 }
8440
8441 /*!
8442 The brush is used to draw basic fills of the plottable representation in the
8443 plot. The Fill can be a color, gradient or texture, see the usage of QBrush.
8444
8445 For example, the \ref QCPGraph subclass draws the fill under the graph with this brush, when
8446 it's not set to Qt::NoBrush.
8447
8448 \see setPen
8449 */
8450 void QCPAbstractPlottable::setBrush(const QBrush &brush)
8451 {
8452 mBrush = brush;
8453 }
8454
8455 /*!
8456 When the plottable is selected, this brush is used to draw fills instead of the normal
8457 brush set via \ref setBrush.
8458
8459 \see setSelected, setSelectable, setSelectedPen, selectTest
8460 */
8461 void QCPAbstractPlottable::setSelectedBrush(const QBrush &brush)
8462 {
8463 mSelectedBrush = brush;
8464 }
8465
8466 /*!
8467 The key axis of a plottable can be set to any axis of a QCustomPlot, as long as it is orthogonal
8468 to the plottable's value axis. This function performs no checks to make sure this is the case.
8469 The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and the
8470 y-axis (QCustomPlot::yAxis) as value axis.
8471
8472 Normally, the key and value axes are set in the constructor of the plottable (or \ref
8473 QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface).
8474
8475 \see setValueAxis
8476 */
8477 void QCPAbstractPlottable::setKeyAxis(QCPAxis *axis)
8478 {
8479 mKeyAxis = axis;
8480 }
8481
8482 /*!
8483 The value axis of a plottable can be set to any axis of a QCustomPlot, as long as it is
8484 orthogonal to the plottable's key axis. This function performs no checks to make sure this is the
8485 case. The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and
8486 the y-axis (QCustomPlot::yAxis) as value axis.
8487
8488 Normally, the key and value axes are set in the constructor of the plottable (or \ref
8489 QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface).
8490
8491 \see setKeyAxis
8492 */
8493 void QCPAbstractPlottable::setValueAxis(QCPAxis *axis)
8494 {
8495 mValueAxis = axis;
8496 }
8497
8498 /*!
8499 Sets whether the user can (de-)select this plottable by clicking on the QCustomPlot surface.
8500 (When \ref QCustomPlot::setInteractions contains iSelectPlottables.)
8501
8502 However, even when \a selectable was set to false, it is possible to set the selection manually,
8503 by calling \ref setSelected directly.
8504
8505 \see setSelected
8506 */
8507 void QCPAbstractPlottable::setSelectable(bool selectable)
8508 {
8509 mSelectable = selectable;
8510 }
8511
8512 /*!
8513 Sets whether this plottable is selected or not. When selected, it uses a different pen and brush
8514 to draw its lines and fills, see \ref setSelectedPen and \ref setSelectedBrush.
8515
8516 The entire selection mechanism for plottables is handled automatically when \ref
8517 QCustomPlot::setInteractions contains iSelectPlottables. You only need to call this function when
8518 you wish to change the selection state manually.
8519
8520 This function can change the selection state even when \ref setSelectable was set to false.
8521
8522 emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
8523
8524 \see selectTest
8525 */
8526 void QCPAbstractPlottable::setSelected(bool selected)
8527 {
8528 if (mSelected != selected)
8529 {
8530 mSelected = selected;
8531 emit selectionChanged(mSelected);
8532 }
8533 }
8534
8535 /*!
8536 Rescales the key and value axes associated with this plottable to contain all displayed data, so
8537 the whole plottable is visible. If the scaling of an axis is logarithmic, rescaleAxes will make
8538 sure not to rescale to an illegal range i.e. a range containing different signs and/or zero.
8539 Instead it will stay in the current sign domain and ignore all parts of the plottable that lie
8540 outside of that domain.
8541
8542 \a onlyEnlarge makes sure the ranges are only expanded, never reduced. So it's possible to show
8543 multiple plottables in their entirety by multiple calls to rescaleAxes where the first call has
8544 \a onlyEnlarge set to false (the default), and all subsequent set to true.
8545 */
8546 void QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const
8547 {
8548 rescaleKeyAxis(onlyEnlarge);
8549 rescaleValueAxis(onlyEnlarge);
8550 }
8551
8552 /*!
8553 Rescales the key axis of the plottable so the whole plottable is visible.
8554
8555 See \ref rescaleAxes for detailed behaviour.
8556 */
8557 void QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const
8558 {
8559 SignDomain signDomain = sdBoth;
8560 if (mKeyAxis->scaleType() == QCPAxis::stLogarithmic)
8561 signDomain = (mKeyAxis->range().upper < 0 ? sdNegative : sdPositive);
8562
8563 bool validRange;
8564 QCPRange newRange = getKeyRange(validRange, signDomain);
8565 if (validRange)
8566 {
8567 if (onlyEnlarge)
8568 {
8569 if (mKeyAxis->range().lower < newRange.lower)
8570 newRange.lower = mKeyAxis->range().lower;
8571 if (mKeyAxis->range().upper > newRange.upper)
8572 newRange.upper = mKeyAxis->range().upper;
8573 }
8574 mKeyAxis->setRange(newRange);
8575 }
8576 }
8577
8578 /*!
8579 Rescales the value axis of the plottable so the whole plottable is visible.
8580
8581 See \ref rescaleAxes for detailed behaviour.
8582 */
8583 void QCPAbstractPlottable::rescaleValueAxis(bool onlyEnlarge) const
8584 {
8585 SignDomain signDomain = sdBoth;
8586 if (mValueAxis->scaleType() == QCPAxis::stLogarithmic)
8587 signDomain = (mValueAxis->range().upper < 0 ? sdNegative : sdPositive);
8588
8589 bool validRange;
8590 QCPRange newRange = getValueRange(validRange, signDomain);
8591
8592 if (validRange)
8593 {
8594 if (onlyEnlarge)
8595 {
8596 if (mValueAxis->range().lower < newRange.lower)
8597 newRange.lower = mValueAxis->range().lower;
8598 if (mValueAxis->range().upper > newRange.upper)
8599 newRange.upper = mValueAxis->range().upper;
8600 }
8601 mValueAxis->setRange(newRange);
8602 }
8603 }
8604
8605 /*!
8606 Adds this plottable to the legend of the parent QCustomPlot.
8607
8608 Normally, a QCPPlottableLegendItem is created and inserted into the legend. If the plottable
8609 needs a more specialized representation in the plot, this function will take this into account
8610 and instead create the specialized subclass of QCPAbstractLegendItem.
8611
8612 Returns true on success, i.e. when a legend item associated with this plottable isn't already in
8613 the legend.
8614
8615 \see removeFromLegend, QCPLegend::addItem
8616 */
8617 bool QCPAbstractPlottable::addToLegend()
8618 {
8619 if (!mParentPlot->legend->hasItemWithPlottable(this))
8620 {
8621 mParentPlot->legend->addItem(new QCPPlottableLegendItem(mParentPlot->legend, this));
8622 return true;
8623 } else
8624 return false;
8625 }
8626
8627 /*!
8628 Removes the plottable from the legend of the parent QCustomPlot. This means the
8629 QCPAbstractLegendItem (usually a QCPPlottableLegendItem) that is associated with this plottable
8630 is removed.
8631
8632 Returns true on success, i.e. if a legend item associated with this plottable was found and
8633 removed from the legend.
8634
8635 \see addToLegend, QCPLegend::removeItem
8636 */
8637 bool QCPAbstractPlottable::removeFromLegend() const
8638 {
8639 if (QCPPlottableLegendItem *lip = mParentPlot->legend->itemWithPlottable(this))
8640 return mParentPlot->legend->removeItem(lip);
8641 else
8642 return false;
8643 }
8644
8645 /* inherits documentation from base class */
8646 QRect QCPAbstractPlottable::clipRect() const
8647 {
8648 return mKeyAxis->axisRect() | mValueAxis->axisRect();
8649 }
8650
8651 /*! \internal
8652
8653 Convenience function for transforming a key/value pair to pixels on the QCustomPlot surface,
8654 taking the orientations of the axes associated with this plottable into account (e.g. whether key
8655 represents x or y).
8656
8657 \a key and \a value are transformed to the coodinates in pixels and are written to \a x and \a y.
8658
8659 \see pixelsToCoords, QCPAxis::coordToPixel
8660 */
8661 void QCPAbstractPlottable::coordsToPixels(double key, double value, double &x, double &y) const
8662 {
8663 if (mKeyAxis->orientation() == Qt::Horizontal)
8664 {
8665 x = mKeyAxis->coordToPixel(key);
8666 y = mValueAxis->coordToPixel(value);
8667 } else
8668 {
8669 y = mKeyAxis->coordToPixel(key);
8670 x = mValueAxis->coordToPixel(value);
8671 }
8672 }
8673
8674 /*! \internal
8675 \overload
8676
8677 Returns the input as pixel coordinates in a QPointF.
8678 */
8679 const QPointF QCPAbstractPlottable::coordsToPixels(double key, double value) const
8680 {
8681 if (mKeyAxis->orientation() == Qt::Horizontal)
8682 return QPointF(mKeyAxis->coordToPixel(key), mValueAxis->coordToPixel(value));
8683 else
8684 return QPointF(mValueAxis->coordToPixel(value), mKeyAxis->coordToPixel(key));
8685 }
8686
8687 /*! \internal
8688
8689 Convenience function for transforming a x/y pixel pair on the QCustomPlot surface to plot coordinates,
8690 taking the orientations of the axes associated with this plottable into account (e.g. whether key
8691 represents x or y).
8692
8693 \a x and \a y are transformed to the plot coodinates and are written to \a key and \a value.
8694
8695 \see coordsToPixels, QCPAxis::coordToPixel
8696 */
8697 void QCPAbstractPlottable::pixelsToCoords(double x, double y, double &key, double &value) const
8698 {
8699 if (mKeyAxis->orientation() == Qt::Horizontal)
8700 {
8701 key = mKeyAxis->pixelToCoord(x);
8702 value = mValueAxis->pixelToCoord(y);
8703 } else
8704 {
8705 key = mKeyAxis->pixelToCoord(y);
8706 value = mValueAxis->pixelToCoord(x);
8707 }
8708 }
8709
8710 /*! \internal
8711 \overload
8712
8713 Returns the pixel input \a pixelPos as plot coordinates \a key and \a value.
8714 */
8715 void QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const
8716 {
8717 pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value);
8718 }
8719
8720 /*! \internal
8721
8722 Returns the pen that should be used for drawing lines of the plottable. Returns mPen when the
8723 graph is not selected and mSelectedPen when it is.
8724 */
8725 QPen QCPAbstractPlottable::mainPen() const
8726 {
8727 return mSelected ? mSelectedPen : mPen;
8728 }
8729
8730 /*! \internal
8731
8732 Returns the brush that should be used for drawing fills of the plottable. Returns mBrush when the
8733 graph is not selected and mSelectedBrush when it is.
8734 */
8735 QBrush QCPAbstractPlottable::mainBrush() const
8736 {
8737 return mSelected ? mSelectedBrush : mBrush;
8738 }
8739
8740 /*! \internal
8741
8742 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
8743 before drawing plottable lines.
8744
8745 This is the antialiasing state the painter passed to the \ref draw method is in by default.
8746
8747 This function takes into account the local setting of the antialiasing flag as well as
8748 the overrides set e.g. with \ref QCustomPlot::setNotAntialiasedElements.
8749
8750 \see setAntialiased, applyFillAntialiasingHint, applyScattersAntialiasingHint, applyErrorBarsAntialiasingHint
8751 */
8752 void QCPAbstractPlottable::applyDefaultAntialiasingHint(QCPPainter *painter) const
8753 {
8754 applyAntialiasingHint(painter, mAntialiased, QCP::aePlottables);
8755 }
8756
8757 /*! \internal
8758
8759 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
8760 before drawing plottable fills.
8761
8762 This function takes into account the local setting of the fill antialiasing flag as well as
8763 the overrides set e.g. with \ref QCustomPlot::setNotAntialiasedElements.
8764
8765 \see setAntialiased, applyDefaultAntialiasingHint, applyScattersAntialiasingHint, applyErrorBarsAntialiasingHint
8766 */
8767 void QCPAbstractPlottable::applyFillAntialiasingHint(QCPPainter *painter) const
8768 {
8769 applyAntialiasingHint(painter, mAntialiasedFill, QCP::aeFills);
8770 }
8771
8772 /*! \internal
8773
8774 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
8775 before drawing plottable scatter points.
8776
8777 This function takes into account the local setting of the scatters antialiasing flag as well as
8778 the overrides set e.g. with \ref QCustomPlot::setNotAntialiasedElements.
8779
8780 \see setAntialiased, applyFillAntialiasingHint, applyDefaultAntialiasingHint, applyErrorBarsAntialiasingHint
8781 */
8782 void QCPAbstractPlottable::applyScattersAntialiasingHint(QCPPainter *painter) const
8783 {
8784 applyAntialiasingHint(painter, mAntialiasedScatters, QCP::aeScatters);
8785 }
8786
8787 /*! \internal
8788
8789 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
8790 before drawing plottable error bars.
8791
8792 This function takes into account the local setting of the error bars antialiasing flag as well as
8793 the overrides set e.g. with \ref QCustomPlot::setNotAntialiasedElements.
8794
8795 \see setAntialiased, applyFillAntialiasingHint, applyScattersAntialiasingHint, applyDefaultAntialiasingHint
8796 */
8797 void QCPAbstractPlottable::applyErrorBarsAntialiasingHint(QCPPainter *painter) const
8798 {
8799 applyAntialiasingHint(painter, mAntialiasedErrorBars, QCP::aeErrorBars);
8800 }
8801
8802 /*! \internal
8803
8804 Finds the shortest squared distance of \a point to the line segment defined by \a start and \a
8805 end.
8806
8807 This function may be used to help with the implementation of the \ref selectTest function for
8808 specific plottables.
8809
8810 \note This function is identical to QCPAbstractItem::distSqrToLine
8811 */
8812 double QCPAbstractPlottable::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
8813 {
8814 QVector2D a(start);
8815 QVector2D b(end);
8816 QVector2D p(point);
8817 QVector2D v(b-a);
8818
8819 double vLengthSqr = v.lengthSquared();
8820 if (!qFuzzyIsNull(vLengthSqr))
8821 {
8822 double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
8823 if (mu < 0)
8824 return (a-p).lengthSquared();
8825 else if (mu > 1)
8826 return (b-p).lengthSquared();
8827 else
8828 return ((a + mu*v)-p).lengthSquared();
8829 } else
8830 return (a-p).lengthSquared();
8831 }
8832
8833
8834 // ================================================================================
8835 // =================== QCPAbstractLegendItem
8836 // ================================================================================
8837
8838 /*! \class QCPAbstractLegendItem
8839 \brief The abstract base class for all items in a QCPLegend.
8840
8841 It defines a very basic interface to items in a QCPLegend. For representing plottables in the
8842 legend, the subclass QCPPlottableLegendItem is more suitable.
8843
8844 Only derive directly from this class when you need absolute freedom (i.e. a legend item that's
8845 not associated with a plottable).
8846
8847 You must implement the following pure virtual functions:
8848 \li \ref draw
8849 \li \ref size
8850
8851 You inherit the following members you may use:
8852 <table>
8853 <tr>
8854 <td>QCPLegend *\b mParentLegend</td>
8855 <td>A pointer to the parent QCPLegend.</td>
8856 </tr><tr>
8857 <td>QFont \b mFont</td>
8858 <td>The generic font of the item. You should use this font for all or at least the most prominent text of the item.</td>
8859 </tr>
8860 </table>
8861 */
8862
8863 /* start documentation of pure virtual functions */
8864
8865 /*! \fn void QCPAbstractLegendItem::draw(QCPPainter *painter, const QRect &rect) const = 0;
8866
8867 Draws this legend item with \a painter inside the specified \a rect. The \a rect typically has
8868 the size which was returned from a preceding \ref size call.
8869 */
8870
8871 /*! \fn QSize QCPAbstractLegendItem::size(const QSize &targetSize) const = 0;
8872
8873 Returns the size this item occupies in the legend. The legend will adapt its layout with the help
8874 of this function. If this legend item can have a variable width (e.g. auto-wrapping text), this
8875 function tries to find a size with a width close to the width of \a targetSize. The height of \a
8876 targetSize only may have meaning in specific sublasses. Typically, it's ignored.
8877 */
8878
8879 /* end documentation of pure virtual functions */
8880 /* start of documentation of signals */
8881
8882 /*! \fn void QCPAbstractLegendItem::selectionChanged(bool selected)
8883
8884 This signal is emitted when the selection state of this legend item has changed, either by user interaction
8885 or by a direct call to \ref setSelected.
8886 */
8887
8888 /* end of documentation of signals */
8889
8890 /*!
8891 Constructs a QCPAbstractLegendItem and associates it with the QCPLegend \a parent. This does not
8892 cause the item to be added to \a parent, so \ref QCPLegend::addItem must be called separately.
8893 */
8894 QCPAbstractLegendItem::QCPAbstractLegendItem(QCPLegend *parent) :
8895 QObject(parent),
8896 mParentLegend(parent),
8897 mAntialiased(false),
8898 mFont(parent->font()),
8899 mTextColor(parent->textColor()),
8900 mSelectedFont(parent->selectedFont()),
8901 mSelectedTextColor(parent->selectedTextColor()),
8902 mSelectable(true),
8903 mSelected(false)
8904 {
8905 }
8906
8907 /*!
8908 Sets whether this legend item is drawn antialiased or not.
8909
8910 Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
8911 QCustomPlot::setNotAntialiasedElements.
8912 */
8913 void QCPAbstractLegendItem::setAntialiased(bool enabled)
8914 {
8915 mAntialiased = enabled;
8916 }
8917
8918 /*!
8919 Sets the default font of this specific legend item to \a font.
8920
8921 \see setTextColor, QCPLegend::setFont
8922 */
8923 void QCPAbstractLegendItem::setFont(const QFont &font)
8924 {
8925 mFont = font;
8926 }
8927
8928 /*!
8929 Sets the default text color of this specific legend item to \a color.
8930
8931 \see setFont, QCPLegend::setTextColor
8932 */
8933 void QCPAbstractLegendItem::setTextColor(const QColor &color)
8934 {
8935 mTextColor = color;
8936 }
8937
8938 /*!
8939 When this legend item is selected, \a font is used to draw generic text, instead of the normal
8940 font set with \ref setFont.
8941
8942 \see setFont, QCPLegend::setSelectedFont
8943 */
8944 void QCPAbstractLegendItem::setSelectedFont(const QFont &font)
8945 {
8946 mSelectedFont = font;
8947 }
8948
8949 /*!
8950 When this legend item is selected, \a color is used to draw generic text, instead of the normal
8951 color set with \ref setTextColor.
8952
8953 \see setTextColor, QCPLegend::setSelectedTextColor
8954 */
8955 void QCPAbstractLegendItem::setSelectedTextColor(const QColor &color)
8956 {
8957 mSelectedTextColor = color;
8958 }
8959
8960 /*!
8961 Sets whether this specific legend item is selectable.
8962
8963 \see setSelected, QCustomPlot::setInteractions
8964 */
8965 void QCPAbstractLegendItem::setSelectable(bool selectable)
8966 {
8967 mSelectable = selectable;
8968 }
8969
8970 /*!
8971 Sets whether this specific legend item is selected. The selection state of the parent QCPLegend
8972 is updated correspondingly.
8973
8974 It is possible to set the selection state of this item by calling this function directly, even if
8975 setSelectable is set to false.
8976
8977 \see setSelectable, QCustomPlot::setInteractions
8978 */
8979 void QCPAbstractLegendItem::setSelected(bool selected)
8980 {
8981 if (mSelected != selected)
8982 {
8983 mSelected = selected;
8984 emit selectionChanged(mSelected);
8985 mParentLegend->updateSelectionState();
8986 }
8987 }
8988
8989 /*! \internal
8990
8991 Sets the QPainter::Antialiasing render hint on the provided \a painter, depending on the \ref
8992 setAntialiased state of this legend item as well as the overrides \ref
8993 QCustomPlot::setAntialiasedElements and \ref QCustomPlot::setNotAntialiasedElements.
8994 */
8995 void QCPAbstractLegendItem::applyAntialiasingHint(QCPPainter *painter) const
8996 {
8997 if (mParentLegend->mParentPlot->notAntialiasedElements().testFlag(QCP::aeLegendItems))
8998 painter->setAntialiasing(false);
8999 else if (mParentLegend->mParentPlot->antialiasedElements().testFlag(QCP::aeLegendItems))
9000 painter->setAntialiasing(true);
9001 else
9002 painter->setAntialiasing(mAntialiased);
9003 }
9004
9005
9006 // ================================================================================
9007 // =================== QCPPlottableLegendItem
9008 // ================================================================================
9009 /*! \class QCPPlottableLegendItem
9010 \brief A legend item representing a plottable with an icon and the plottable name.
9011
9012 This is the standard legend item for plottables. It displays an icon of the plottable next to the
9013 plottable name. The icon is drawn by the respective plottable itself (\ref
9014 QCPAbstractPlottable::drawLegendIcon), and tries to give an intuitive symbol for the plottable.
9015 For example, the QCPGraph draws a centered horizontal line with a single scatter point in the
9016 middle and filling (if enabled) below.
9017
9018 Legend items of this type are always associated with one plottable (retrievable via the
9019 plottable() function and settable with the constructor). You may change the font of the plottable
9020 name with \ref setFont. If \ref setTextWrap is set to true, the plottable name will wrap at the
9021 right legend boundary (see \ref QCPLegend::setMinimumSize). Icon padding and border pen is taken
9022 from the parent QCPLegend, see \ref QCPLegend::setIconBorderPen and \ref
9023 QCPLegend::setIconTextPadding.
9024
9025 The function \ref QCPAbstractPlottable::addToLegend/\ref QCPAbstractPlottable::removeFromLegend
9026 creates/removes legend items of this type in the default implementation. However, these functions
9027 may be reimplemented such that a different kind of legend item (e.g a direct subclass of
9028 QCPAbstractLegendItem) is used for that plottable.
9029 */
9030
9031 /*!
9032 Creates a new legend item associated with \a plottable.
9033
9034 Once it's created, it can be added to the legend via \ref QCPLegend::addItem.
9035
9036 A more convenient way of adding/removing a plottable to/from the legend is via the functions \ref
9037 QCPAbstractPlottable::addToLegend and \ref QCPAbstractPlottable::removeFromLegend.
9038 */
9039 QCPPlottableLegendItem::QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable) :
9040 QCPAbstractLegendItem(parent),
9041 mPlottable(plottable),
9042 mTextWrap(false)
9043 {
9044 }
9045
9046 /*!
9047 Sets whether the text of the legend item is wrapped at word boundaries to fit the with of the
9048 legend.
9049
9050 To prevent the legend autoSize feature (QCPLegend::setAutoSize) from compressing the text too
9051 strong by wrapping it very often, set an appropriate minimum width with
9052 QCPLegend::setMinimumSize.
9053 */
9054 void QCPPlottableLegendItem::setTextWrap(bool wrap)
9055 {
9056 mTextWrap = wrap;
9057 }
9058
9059 /*! \internal
9060
9061 Returns the pen that shall be used to draw the icon border, taking into account the selection
9062 state of this item.
9063 */
9064 QPen QCPPlottableLegendItem::getIconBorderPen() const
9065 {
9066 return mSelected ? mParentLegend->selectedIconBorderPen() : mParentLegend->iconBorderPen();
9067 }
9068
9069 /*! \internal
9070
9071 Returns the text color that shall be used to draw text, taking into account the selection state
9072 of this item.
9073 */
9074 QColor QCPPlottableLegendItem::getTextColor() const
9075 {
9076 return mSelected ? mSelectedTextColor : mTextColor;
9077 }
9078
9079 /*! \internal
9080
9081 Returns the font that shall be used to draw text, taking into account the selection state of this
9082 item.
9083 */
9084 QFont QCPPlottableLegendItem::getFont() const
9085 {
9086 return mSelected ? mSelectedFont : mFont;
9087 }
9088
9089 /*! \internal
9090
9091 Draws the item with \a painter into \a rect.
9092
9093 The width of the passed rect is used as text wrapping width, when \ref setTextWrap is enabled.
9094 The height is ignored. The rect is not used as a clipping rect (overpainting is not prevented
9095 inside this function), so you should set an appropriate clipping rect on the painter before
9096 calling this function. Ideally, the width of the rect should be the result of a preceding call to
9097 \ref size.
9098 */
9099 void QCPPlottableLegendItem::draw(QCPPainter *painter, const QRect &rect) const
9100 {
9101 if (!mPlottable) return;
9102 painter->setFont(getFont());
9103 painter->setPen(QPen(getTextColor()));
9104 int iconTextPadding = mParentLegend->iconTextPadding();
9105 QSize iconSize = mParentLegend->iconSize();
9106 QRect textRect;
9107 QRect iconRect(rect.topLeft(), iconSize);
9108 if (mTextWrap)
9109 {
9110 // take width from rect since our text should wrap there (only icon must fit at least):
9111 textRect = painter->fontMetrics().boundingRect(0, 0, rect.width()-iconTextPadding-iconSize.width(), rect.height(), Qt::TextDontClip | Qt::TextWordWrap, mPlottable->name());
9112 if (textRect.height() < iconSize.height()) // text smaller than icon, center text vertically in icon height
9113 {
9114 painter->drawText(rect.x()+iconSize.width()+iconTextPadding, rect.y(), rect.width()-iconTextPadding-iconSize.width(), iconSize.height(), Qt::TextDontClip | Qt::TextWordWrap, mPlottable->name());
9115 } else // text bigger than icon, position top of text with top of icon
9116 {
9117 painter->drawText(rect.x()+iconSize.width()+iconTextPadding, rect.y(), rect.width()-iconTextPadding-iconSize.width(), textRect.height(), Qt::TextDontClip | Qt::TextWordWrap, mPlottable->name());
9118 }
9119 } else
9120 {
9121 // text can't wrap (except with explicit newlines), center at current item size (icon size)
9122 textRect = painter->fontMetrics().boundingRect(0, 0, 0, rect.height(), Qt::TextDontClip, mPlottable->name());
9123 if (textRect.height() < iconSize.height()) // text smaller than icon, center text vertically in icon height
9124 {
9125 painter->drawText(rect.x()+iconSize.width()+iconTextPadding, rect.y(), rect.width(), iconSize.height(), Qt::TextDontClip, mPlottable->name());
9126 } else // text bigger than icon, position top of text with top of icon
9127 {
9128 painter->drawText(rect.x()+iconSize.width()+iconTextPadding, rect.y(), rect.width(), textRect.height(), Qt::TextDontClip, mPlottable->name());
9129 }
9130 }
9131 // draw icon:
9132 painter->save();
9133 painter->setClipRect(iconRect, Qt::IntersectClip);
9134 mPlottable->drawLegendIcon(painter, iconRect);
9135 painter->restore();
9136 // draw icon border:
9137 if (getIconBorderPen().style() != Qt::NoPen)
9138 {
9139 painter->setPen(getIconBorderPen());
9140 painter->setBrush(Qt::NoBrush);
9141 painter->drawRect(iconRect);
9142 }
9143 }
9144
9145 /*! \internal
9146
9147 Calculates and returns the size of this item. If \ref setTextWrap is enabled, the width of \a
9148 targetSize will be used as the text wrapping width. This does not guarantee, that the width of
9149 the returned QSize is the same as the width of \a targetSize, since wrapping occurs only at word
9150 boundaries. So a single word that extends beyond the width of \a targetSize, will stretch the
9151 returned QSize accordingly.
9152
9153 The height of \a targetSize is ignored. The height of the returned QSize is either the height
9154 of the icon or the height of the text bounding box, whichever is larger.
9155 */
9156 QSize QCPPlottableLegendItem::size(const QSize &targetSize) const
9157 {
9158 if (!mPlottable) return QSize();
9159 QSize result(0, 0);
9160 QRect textRect;
9161 QFontMetrics fontMetrics(getFont());
9162 int iconTextPadding = mParentLegend->iconTextPadding();
9163 QSize iconSize = mParentLegend->iconSize();
9164 if (mTextWrap)
9165 {
9166 // take width from targetSize since our text can wrap (Only icon must fit at least):
9167 textRect = fontMetrics.boundingRect(0, 0, targetSize.width()-iconTextPadding-iconSize.width(), iconSize.height(), Qt::TextDontClip | Qt::TextWordWrap, mPlottable->name());
9168 } else
9169 {
9170 // text can't wrap (except with explicit newlines), center at current item size (icon size)
9171 textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
9172 }
9173 result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width());
9174 result.setHeight(qMax(textRect.height(), iconSize.height()));
9175 return result;
9176 }
9177
9178 // ================================================================================
9179 // =================== QCPCurve
9180 // ================================================================================
9181 /*! \class QCPCurve
9182 \brief A plottable representing a parametric curve in a plot.
9183
9184 To plot data, assign it with the \ref setData or \ref addData functions.
9185
9186 \section appearance Changing the appearance
9187
9188 The appearance of the curve is determined by the pen and the brush (\ref setPen, \ref setBrush).
9189 \section usage Usage
9190
9191 Like all data representing objects in QCustomPlot, the QCPCurve is a plottable (QCPAbstractPlottable). So
9192 the plottable-interface of QCustomPlot applies (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
9193
9194 Usually, you first create an instance:
9195 \code
9196 QCPCurve *newCurve = new QCPCurve(customPlot->xAxis, customPlot->yAxis);\endcode
9197 add it to the customPlot with QCustomPlot::addPlottable:
9198 \code
9199 customPlot->addPlottable(newCurve);\endcode
9200 and then modify the properties of the newly created plottable, e.g.:
9201 \code
9202 newCurve->setName("Fermat's Spiral");
9203 newCurve->setData(tData, xData, yData);\endcode
9204 */
9205
9206 /*!
9207 Constructs a curve which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
9208 axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
9209 the same orientation. If either of these restrictions is violated, a corresponding message is
9210 printed to the debug output (qDebug), the construction is not aborted, though.
9211
9212 The constructed QCPCurve can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
9213 then takes ownership of the graph.
9214 */
9215 QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) :
9216 QCPAbstractPlottable(keyAxis, valueAxis)
9217 {
9218 mData = new QCPCurveDataMap;
9219 mPen.setColor(Qt::blue);
9220 mPen.setStyle(Qt::SolidLine);
9221 mBrush.setColor(Qt::blue);
9222 mBrush.setStyle(Qt::NoBrush);
9223 mSelectedPen = mPen;
9224 mSelectedPen.setWidthF(2.5);
9225 mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
9226 mSelectedBrush = mBrush;
9227
9228 setScatterSize(6);
9229 setScatterStyle(QCP::ssNone);
9230 setLineStyle(lsLine);
9231 }
9232
9233 QCPCurve::~QCPCurve()
9234 {
9235 delete mData;
9236 }
9237
9238 /*!
9239 Replaces the current data with the provided \a data.
9240
9241 If \a copy is set to true, data points in \a data will only be copied. if false, the plottable
9242 takes ownership of the passed data and replaces the internal data pointer with it. This is
9243 significantly faster than copying for large datasets.
9244 */
9245 void QCPCurve::setData(QCPCurveDataMap *data, bool copy)
9246 {
9247 if (copy)
9248 {
9249 *mData = *data;
9250 } else
9251 {
9252 delete mData;
9253 mData = data;
9254 }
9255 }
9256
9257 /*! \overload
9258
9259 Replaces the current data with the provided points in \a t, \a key and \a value tuples. The
9260 provided vectors should have equal length. Else, the number of added points will be the size of
9261 the smallest vector.
9262 */
9263 void QCPCurve::setData(const QVector<double> &t, const QVector<double> &key, const QVector<double> &value)
9264 {
9265 mData->clear();
9266 int n = t.size();
9267 n = qMin(n, key.size());
9268 n = qMin(n, value.size());
9269 QCPCurveData newData;
9270 for (int i=0; i<n; ++i)
9271 {
9272 newData.t = t[i];
9273 newData.key = key[i];
9274 newData.value = value[i];
9275 mData->insertMulti(newData.t, newData);
9276 }
9277 }
9278
9279 /*! \overload
9280
9281 Replaces the current data with the provided \a key and \a value pairs. The t parameter
9282 of each data point will be set to the integer index of the respective key/value pair.
9283 */
9284 void QCPCurve::setData(const QVector<double> &key, const QVector<double> &value)
9285 {
9286 mData->clear();
9287 int n = key.size();
9288 n = qMin(n, value.size());
9289 QCPCurveData newData;
9290 for (int i=0; i<n; ++i)
9291 {
9292 newData.t = i; // no t vector given, so we assign t the index of the key/value pair
9293 newData.key = key[i];
9294 newData.value = value[i];
9295 mData->insertMulti(newData.t, newData);
9296 }
9297 }
9298
9299 /*!
9300 Sets the visual appearance of single data points in the plot. If set to \ref QCP::ssNone, no scatter points
9301 are drawn (e.g. for line-only-plots with appropriate line style).
9302 \see ScatterStyle, setLineStyle
9303 */
9304 void QCPCurve::setScatterStyle(QCP::ScatterStyle style)
9305 {
9306 mScatterStyle = style;
9307 }
9308
9309 /*!
9310 This defines how big (in pixels) single scatters are drawn, if scatter style (\ref
9311 setScatterStyle) isn't \ref QCP::ssNone, \ref QCP::ssDot or \ref QCP::ssPixmap. Floating point values are
9312 allowed for fine grained control over optical appearance with antialiased painting.
9313
9314 \see ScatterStyle
9315 */
9316 void QCPCurve::setScatterSize(double size)
9317 {
9318 mScatterSize = size;
9319 }
9320
9321 /*!
9322 If the scatter style (\ref setScatterStyle) is set to ssPixmap, this function defines the QPixmap
9323 that will be drawn centered on the data point coordinate.
9324
9325 \see ScatterStyle
9326 */
9327 void QCPCurve::setScatterPixmap(const QPixmap &pixmap)
9328 {
9329 mScatterPixmap = pixmap;
9330 }
9331
9332 /*!
9333 Sets how the single data points are connected in the plot or how they are represented visually
9334 apart from the scatter symbol. For scatter-only plots, set \a style to \ref lsNone and \ref
9335 setScatterStyle to the desired scatter style.
9336
9337 \see setScatterStyle
9338 */
9339 void QCPCurve::setLineStyle(QCPCurve::LineStyle style)
9340 {
9341 mLineStyle = style;
9342 }
9343
9344 /*!
9345 Adds the provided data points in \a dataMap to the current data.
9346 \see removeData
9347 */
9348 void QCPCurve::addData(const QCPCurveDataMap &dataMap)
9349 {
9350 mData->unite(dataMap);
9351 }
9352
9353 /*! \overload
9354 Adds the provided single data point in \a data to the current data.
9355 \see removeData
9356 */
9357 void QCPCurve::addData(const QCPCurveData &data)
9358 {
9359 mData->insertMulti(data.t, data);
9360 }
9361
9362 /*! \overload
9363 Adds the provided single data point as \a t, \a key and \a value tuple to the current data
9364 \see removeData
9365 */
9366 void QCPCurve::addData(double t, double key, double value)
9367 {
9368 QCPCurveData newData;
9369 newData.t = t;
9370 newData.key = key;
9371 newData.value = value;
9372 mData->insertMulti(newData.t, newData);
9373 }
9374
9375 /*! \overload
9376
9377 Adds the provided single data point as \a key and \a value pair to the current data The t
9378 parameter of the data point is set to the t of the last data point plus 1. If there is no last
9379 data point, t will be set to 0.
9380
9381 \see removeData
9382 */
9383 void QCPCurve::addData(double key, double value)
9384 {
9385 QCPCurveData newData;
9386 if (!mData->isEmpty())
9387 newData.t = (mData->constEnd()-1).key()+1;
9388 else
9389 newData.t = 0;
9390 newData.key = key;
9391 newData.value = value;
9392 mData->insertMulti(newData.t, newData);
9393 }
9394
9395 /*! \overload
9396 Adds the provided data points as \a t, \a key and \a value tuples to the current data.
9397 \see removeData
9398 */
9399 void QCPCurve::addData(const QVector<double> &ts, const QVector<double> &keys, const QVector<double> &values)
9400 {
9401 int n = ts.size();
9402 n = qMin(n, keys.size());
9403 n = qMin(n, values.size());
9404 QCPCurveData newData;
9405 for (int i=0; i<n; ++i)
9406 {
9407 newData.t = ts[i];
9408 newData.key = keys[i];
9409 newData.value = values[i];
9410 mData->insertMulti(newData.t, newData);
9411 }
9412 }
9413
9414 /*!
9415 Removes all data points with curve parameter t smaller than \a t.
9416 \see addData, clearData
9417 */
9418 void QCPCurve::removeDataBefore(double t)
9419 {
9420 QCPCurveDataMap::iterator it = mData->begin();
9421 while (it != mData->end() && it.key() < t)
9422 it = mData->erase(it);
9423 }
9424
9425 /*!
9426 Removes all data points with curve parameter t greater than \a t.
9427 \see addData, clearData
9428 */
9429 void QCPCurve::removeDataAfter(double t)
9430 {
9431 if (mData->isEmpty()) return;
9432 QCPCurveDataMap::iterator it = mData->upperBound(t);
9433 while (it != mData->end())
9434 it = mData->erase(it);
9435 }
9436
9437 /*!
9438 Removes all data points with curve parameter t between \a fromt and \a tot. if \a fromt is
9439 greater or equal to \a tot, the function does nothing. To remove a single data point with known
9440 t, use \ref removeData(double t).
9441
9442 \see addData, clearData
9443 */
9444 void QCPCurve::removeData(double fromt, double tot)
9445 {
9446 if (fromt >= tot || mData->isEmpty()) return;
9447 QCPCurveDataMap::iterator it = mData->upperBound(fromt);
9448 QCPCurveDataMap::iterator itEnd = mData->upperBound(tot);
9449 while (it != itEnd)
9450 it = mData->erase(it);
9451 }
9452
9453 /*! \overload
9454
9455 Removes a single data point at curve parameter \a t. If the position is not known with absolute
9456 precision, consider using \ref removeData(double fromt, double tot) with a small fuzziness
9457 interval around the suspected position, depeding on the precision with which the curve parameter
9458 is known.
9459
9460 \see addData, clearData
9461 */
9462 void QCPCurve::removeData(double t)
9463 {
9464 mData->remove(t);
9465 }
9466
9467 /*!
9468 Removes all data points.
9469 \see removeData, removeDataAfter, removeDataBefore
9470 */
9471 void QCPCurve::clearData()
9472 {
9473 mData->clear();
9474 }
9475
9476 /* inherits documentation from base class */
9477 double QCPCurve::selectTest(const QPointF &pos) const
9478 {
9479 if (mData->isEmpty() || !mVisible)
9480 return -1;
9481
9482 return pointDistance(pos);
9483 }
9484
9485 /* inherits documentation from base class */
9486 void QCPCurve::draw(QCPPainter *painter)
9487 {
9488 if (mData->isEmpty()) return;
9489
9490 // allocate line vector:
9491 QVector<QPointF> *lineData = new QVector<QPointF>;
9492 // fill with curve data:
9493 getCurveData(lineData);
9494 // draw curve fill:
9495 if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
9496 {
9497 applyFillAntialiasingHint(painter);
9498 painter->setPen(Qt::NoPen);
9499 painter->setBrush(mainBrush());
9500 painter->drawPolygon(QPolygonF(*lineData));
9501 }
9502 // draw curve line:
9503 if (mLineStyle != lsNone && mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
9504 {
9505 applyDefaultAntialiasingHint(painter);
9506 painter->setPen(mainPen());
9507 painter->setBrush(Qt::NoBrush);
9508 // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
9509 if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
9510 painter->pen().style() == Qt::SolidLine &&
9511 !painter->pdfExportMode())
9512 {
9513 for (int i=1; i<lineData->size(); ++i)
9514 painter->drawLine(lineData->at(i-1), lineData->at(i));
9515 } else
9516 {
9517 painter->drawPolyline(QPolygonF(*lineData));
9518 }
9519 }
9520 // draw scatters:
9521 if (mScatterStyle != QCP::ssNone)
9522 drawScatterPlot(painter, lineData);
9523 // free allocated line data:
9524 delete lineData;
9525 }
9526
9527 /* inherits documentation from base class */
9528 void QCPCurve::drawLegendIcon(QCPPainter *painter, const QRect &rect) const
9529 {
9530 // draw fill:
9531 if (mBrush.style() != Qt::NoBrush)
9532 {
9533 applyFillAntialiasingHint(painter);
9534 painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
9535 }
9536 // draw line vertically centered:
9537 if (mLineStyle != lsNone)
9538 {
9539 applyDefaultAntialiasingHint(painter);
9540 painter->setPen(mPen);
9541 painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens
9542 }
9543 // draw scatter symbol:
9544 if (mScatterStyle != QCP::ssNone)
9545 {
9546 if (mScatterStyle == QCP::ssPixmap && (mScatterPixmap.size().width() > rect.width() || mScatterPixmap.size().height() > rect.height()))
9547 {
9548 // handle pixmap scatters that are larger than legend icon rect separately.
9549 // We resize them and draw them manually, instead of calling drawScatter:
9550 QSize newSize = mScatterPixmap.size();
9551 newSize.scale(rect.size(), Qt::KeepAspectRatio);
9552 QRect targetRect;
9553 targetRect.setSize(newSize);
9554 targetRect.moveCenter(rect.center());
9555 bool smoothBackup = painter->testRenderHint(QPainter::SmoothPixmapTransform);
9556 painter->setRenderHint(QPainter::SmoothPixmapTransform, true);
9557 painter->drawPixmap(targetRect, mScatterPixmap);
9558 painter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);
9559 } else
9560 {
9561 applyScattersAntialiasingHint(painter);
9562 painter->setPen(mPen);
9563 painter->drawScatter(QRectF(rect).center().x(), QRectF(rect).center().y(), mScatterSize, mScatterStyle);
9564 }
9565 }
9566 }
9567
9568 /*! \internal
9569
9570 Draws scatter symbols at every data point passed in \a pointData. scatter symbols are independent of
9571 the line style and are always drawn if scatter style is not \ref QCP::ssNone.
9572 */
9573 void QCPCurve::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> *pointData) const
9574 {
9575 // draw scatter point symbols:
9576 applyScattersAntialiasingHint(painter);
9577 painter->setPen(mainPen());
9578 painter->setBrush(mainBrush());
9579 painter->setScatterPixmap(mScatterPixmap);
9580 for (int i=0; i<pointData->size(); ++i)
9581 painter->drawScatter(pointData->at(i).x(), pointData->at(i).y(), mScatterSize, mScatterStyle);
9582 }
9583
9584 /*! \internal
9585
9586 called by QCPCurve::draw to generate a point vector (pixels) which represents the line of the
9587 curve. Line segments that aren't visible in the current axis rect are handled in an optimized
9588 way.
9589 */
9590 void QCPCurve::getCurveData(QVector<QPointF> *lineData) const
9591 {
9592 /* Extended sides of axis rect R divide space into 9 regions:
9593 1__|_4_|__7
9594 2__|_R_|__8
9595 3 | 6 | 9
9596 General idea: If the two points of a line segment are in the same region (that is not R), the line segment corner is removed.
9597 Curves outside R become straight lines closely outside of R which greatly reduces drawing time, yet keeps the look of lines and
9598 fills inside R consistent.
9599 The region R has index 5.
9600 */
9601 lineData->reserve(mData->size());
9602 QCPCurveDataMap::const_iterator it;
9603 int lastRegion = 5;
9604 int currentRegion = 5;
9605 double RLeft = mKeyAxis->range().lower;
9606 double RRight = mKeyAxis->range().upper;
9607 double RBottom = mValueAxis->range().lower;
9608 double RTop = mValueAxis->range().upper;
9609 double x, y; // current key/value
9610 bool addedLastAlready = true;
9611 bool firstPoint = true; // first point must always be drawn, to make sure fill works correctly
9612 for (it = mData->constBegin(); it != mData->constEnd(); ++it)
9613 {
9614 x = it.value().key;
9615 y = it.value().value;
9616 // determine current region:
9617 if (x < RLeft) // region 123
9618 {
9619 if (y > RTop)
9620 currentRegion = 1;
9621 else if (y < RBottom)
9622 currentRegion = 3;
9623 else
9624 currentRegion = 2;
9625 } else if (x > RRight) // region 789
9626 {
9627 if (y > RTop)
9628 currentRegion = 7;
9629 else if (y < RBottom)
9630 currentRegion = 9;
9631 else
9632 currentRegion = 8;
9633 } else // region 456
9634 {
9635 if (y > RTop)
9636 currentRegion = 4;
9637 else if (y < RBottom)
9638 currentRegion = 6;
9639 else
9640 currentRegion = 5;
9641 }
9642
9643 /*
9644 Watch out, the next part is very tricky. It modifies the curve such that it seems like the
9645 whole thing is still drawn, but actually the points outside the axisRect are simplified
9646 ("optimized") greatly. There are some subtle special cases when line segments are large and
9647 thereby each subsequent point may be in a different region or even skip some.
9648 */
9649 // determine whether to keep current point:
9650 if (currentRegion == 5 || (firstPoint && mBrush.style() != Qt::NoBrush)) // current is in R, add current and last if it wasn't added already
9651 {
9652 if (!addedLastAlready) // in case curve just entered R, make sure the last point outside R is also drawn correctly
9653 lineData->append(coordsToPixels((it-1).value().key, (it-1).value().value)); // add last point to vector
9654 else if (lastRegion != 5) // added last already. If that's the case, we probably added it at optimized position. So go back and make sure it's at original position (else the angle changes under which this segment enters R)
9655 {
9656 if (!firstPoint) // because on firstPoint, currentRegion is 5 and addedLastAlready is true, although there is no last point
9657 lineData->replace(lineData->size()-1, coordsToPixels((it-1).value().key, (it-1).value().value));
9658 }
9659 lineData->append(coordsToPixels(it.value().key, it.value().value)); // add current point to vector
9660 addedLastAlready = true; // so in next iteration, we don't add this point twice
9661 } else if (currentRegion != lastRegion) // changed region, add current and last if not added already
9662 {
9663 // using outsideCoordsToPixels instead of coorsToPixels for optimized point placement (places points just outside axisRect instead of potentially far away)
9664
9665 // if we're coming from R or we skip diagonally over the corner regions (so line might still be visible in R), we can't place points optimized
9666 if (lastRegion == 5 || // coming from R
9667 ((lastRegion==2 && currentRegion==4) || (lastRegion==4 && currentRegion==2)) || // skip top left diagonal
9668 ((lastRegion==4 && currentRegion==8) || (lastRegion==8 && currentRegion==4)) || // skip top right diagonal
9669 ((lastRegion==8 && currentRegion==6) || (lastRegion==6 && currentRegion==8)) || // skip bottom right diagonal
9670 ((lastRegion==6 && currentRegion==2) || (lastRegion==2 && currentRegion==6)) // skip bottom left diagonal
9671 )
9672 {
9673 // always add last point if not added already, original:
9674 if (!addedLastAlready)
9675 lineData->append(coordsToPixels((it-1).value().key, (it-1).value().value));
9676 // add current point, original:
9677 lineData->append(coordsToPixels(it.value().key, it.value().value));
9678 } else // no special case that forbids optimized point placement, so do it:
9679 {
9680 // always add last point if not added already, optimized:
9681 if (!addedLastAlready)
9682 lineData->append(outsideCoordsToPixels((it-1).value().key, (it-1).value().value, currentRegion));
9683 // add current point, optimized:
9684 lineData->append(outsideCoordsToPixels(it.value().key, it.value().value, currentRegion));
9685 }
9686 addedLastAlready = true; // so that if next point enters 5, or crosses another region boundary, we don't add this point twice
9687 } else // neither in R, nor crossed a region boundary, skip current point
9688 {
9689 addedLastAlready = false;
9690 }
9691 lastRegion = currentRegion;
9692 firstPoint = false;
9693 }
9694 // If curve ends outside R, we want to add very last point so the fill looks like it should when the curve started inside R:
9695 if (lastRegion != 5 && mBrush.style() != Qt::NoBrush && !mData->isEmpty())
9696 lineData->append(coordsToPixels((mData->constEnd()-1).value().key, (mData->constEnd()-1).value().value));
9697 }
9698
9699 /*! \internal
9700
9701 Calculates the (minimum) distance (in pixels) the curve's representation has from the given \a
9702 pixelPoint in pixels. This is used to determine whether the curve was clicked or not, e.g. in
9703 \ref selectTest.
9704 */
9705 double QCPCurve::pointDistance(const QPointF &pixelPoint) const
9706 {
9707 if (mData->isEmpty())
9708 {
9709 qDebug() << Q_FUNC_INFO << "requested point distance on curve" << mName << "without data";
9710 return 500;
9711 }
9712 if (mData->size() == 1)
9713 {
9714 QPointF dataPoint = coordsToPixels(mData->constBegin().key(), mData->constBegin().value().value);
9715 return QVector2D(dataPoint-pixelPoint).length();
9716 }
9717
9718 // calculate minimum distance to line segments:
9719 QVector<QPointF> *lineData = new QVector<QPointF>;
9720 getCurveData(lineData);
9721 double minDistSqr = std::numeric_limits<double>::max();
9722 for (int i=0; i<lineData->size()-1; ++i)
9723 {
9724 double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
9725 if (currentDistSqr < minDistSqr)
9726 minDistSqr = currentDistSqr;
9727 }
9728 delete lineData;
9729 return sqrt(minDistSqr);
9730 }
9731
9732 /*! \internal
9733
9734 This is a specialized \ref coordsToPixels function for points that are outside the visible
9735 axisRect and just crossing a boundary (since \ref getCurveData reduces non-visible curve segments
9736 to those line segments that cross region boundaries, see documentation there). It only uses the
9737 coordinate parallel to the region boundary of the axisRect. The other coordinate is picked 10
9738 pixels outside the axisRect. Together with the optimization in \ref getCurveData this improves
9739 performance for large curves (or zoomed in ones) significantly while keeping the illusion the
9740 whole curve and its filling is still being drawn for the viewer.
9741 */
9742 QPointF QCPCurve::outsideCoordsToPixels(double key, double value, int region) const
9743 {
9744 int margin = 10;
9745 QRect axisRect = mKeyAxis->axisRect() | mValueAxis->axisRect();
9746 QPointF result = coordsToPixels(key, value);
9747 switch (region)
9748 {
9749 case 2: result.setX(axisRect.left()-margin); break; // left
9750 case 8: result.setX(axisRect.right()+margin); break; // right
9751 case 4: result.setY(axisRect.top()-margin); break; // top
9752 case 6: result.setY(axisRect.bottom()+margin); break; // bottom
9753 case 1: result.setX(axisRect.left()-margin);
9754 result.setY(axisRect.top()-margin); break; // top left
9755 case 7: result.setX(axisRect.right()+margin);
9756 result.setY(axisRect.top()-margin); break; // top right
9757 case 9: result.setX(axisRect.right()+margin);
9758 result.setY(axisRect.bottom()+margin); break; // bottom right
9759 case 3: result.setX(axisRect.left()-margin);
9760 result.setY(axisRect.bottom()+margin); break; // bottom left
9761 }
9762 return result;
9763 }
9764
9765 /* inherits documentation from base class */
9766 QCPRange QCPCurve::getKeyRange(bool &validRange, SignDomain inSignDomain) const
9767 {
9768 QCPRange range;
9769 bool haveLower = false;
9770 bool haveUpper = false;
9771
9772 double current;
9773
9774 QCPCurveDataMap::const_iterator it = mData->constBegin();
9775 while (it != mData->constEnd())
9776 {
9777 current = it.value().key;
9778 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
9779 {
9780 if (current < range.lower || !haveLower)
9781 {
9782 range.lower = current;
9783 haveLower = true;
9784 }
9785 if (current > range.upper || !haveUpper)
9786 {
9787 range.upper = current;
9788 haveUpper = true;
9789 }
9790 }
9791 ++it;
9792 }
9793
9794 validRange = haveLower && haveUpper;
9795 return range;
9796 }
9797
9798 /* inherits documentation from base class */
9799 QCPRange QCPCurve::getValueRange(bool &validRange, SignDomain inSignDomain) const
9800 {
9801 QCPRange range;
9802 bool haveLower = false;
9803 bool haveUpper = false;
9804
9805 double current;
9806
9807 QCPCurveDataMap::const_iterator it = mData->constBegin();
9808 while (it != mData->constEnd())
9809 {
9810 current = it.value().value;
9811 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
9812 {
9813 if (current < range.lower || !haveLower)
9814 {
9815 range.lower = current;
9816 haveLower = true;
9817 }
9818 if (current > range.upper || !haveUpper)
9819 {
9820 range.upper = current;
9821 haveUpper = true;
9822 }
9823 }
9824 ++it;
9825 }
9826
9827 validRange = haveLower && haveUpper;
9828 return range;
9829 }
9830
9831 // ================================================================================
9832 // =================== QCPBars
9833 // ================================================================================
9834 /*! \class QCPBars
9835 \brief A plottable representing a bar chart in a plot.
9836
9837 To plot data, assign it with the \ref setData or \ref addData functions.
9838
9839 \section appearance Changing the appearance
9840
9841 The appearance of the bars is determined by the pen and the brush (\ref setPen, \ref setBrush).
9842
9843 Bar charts are stackable. This means, Two QCPBars plottables can be placed on top of each other
9844 (see \ref QCPBars::moveAbove). Then, when two bars are at the same key position, they will appear
9845 stacked.
9846
9847 \section usage Usage
9848
9849 Like all data representing objects in QCustomPlot, the QCPBars is a plottable
9850 (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies
9851 (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
9852
9853 Usually, you first create an instance:
9854 \code
9855 QCPBars *newBars = new QCPBars(customPlot->xAxis, customPlot->yAxis);\endcode
9856 add it to the customPlot with QCustomPlot::addPlottable:
9857 \code
9858 customPlot->addPlottable(newBars);\endcode
9859 and then modify the properties of the newly created plottable, e.g.:
9860 \code
9861 newBars->setName("Country population");
9862 newBars->setData(xData, yData);\endcode
9863 */
9864
9865 /*! \fn QCPBars *QCPBars::barBelow() const
9866 Returns the bars plottable that is directly below this bars plottable.
9867 If there is no such plottable, returns 0.
9868
9869 \see barAbove, moveBelow, moveAbove
9870 */
9871
9872 /*! \fn QCPBars *QCPBars::barAbove() const
9873 Returns the bars plottable that is directly above this bars plottable.
9874 If there is no such plottable, returns 0.
9875
9876 \see barBelow, moveBelow, moveAbove
9877 */
9878
9879 /*!
9880 Constructs a bar chart which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
9881 axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
9882 the same orientation. If either of these restrictions is violated, a corresponding message is
9883 printed to the debug output (qDebug), the construction is not aborted, though.
9884
9885 The constructed QCPBars can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
9886 then takes ownership of the bar chart.
9887 */
9888 QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) :
9889 QCPAbstractPlottable(keyAxis, valueAxis),
9890 mBarBelow(0),
9891 mBarAbove(0)
9892 {
9893 mData = new QCPBarDataMap;
9894 mPen.setColor(Qt::blue);
9895 mPen.setStyle(Qt::SolidLine);
9896 mBrush.setColor(QColor(40, 50, 255, 30));
9897 mBrush.setStyle(Qt::SolidPattern);
9898 mSelectedPen = mPen;
9899 mSelectedPen.setWidthF(2.5);
9900 mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
9901 mSelectedBrush = mBrush;
9902
9903 mWidth = 0.75;
9904 }
9905
9906 QCPBars::~QCPBars()
9907 {
9908 if (mBarBelow || mBarAbove)
9909 connectBars(mBarBelow, mBarAbove); // take this bar out of any stacking
9910 delete mData;
9911 }
9912
9913 /*!
9914 Sets the width of the bars in plot (key) coordinates.
9915 */
9916 void QCPBars::setWidth(double width)
9917 {
9918 mWidth = width;
9919 }
9920
9921 /*!
9922 Replaces the current data with the provided \a data.
9923
9924 If \a copy is set to true, data points in \a data will only be copied. if false, the plottable
9925 takes ownership of the passed data and replaces the internal data pointer with it. This is
9926 significantly faster than copying for large datasets.
9927 */
9928 void QCPBars::setData(QCPBarDataMap *data, bool copy)
9929 {
9930 if (copy)
9931 {
9932 *mData = *data;
9933 } else
9934 {
9935 delete mData;
9936 mData = data;
9937 }
9938 }
9939
9940 /*! \overload
9941
9942 Replaces the current data with the provided points in \a key and \a value tuples. The
9943 provided vectors should have equal length. Else, the number of added points will be the size of
9944 the smallest vector.
9945 */
9946 void QCPBars::setData(const QVector<double> &key, const QVector<double> &value)
9947 {
9948 mData->clear();
9949 int n = key.size();
9950 n = qMin(n, value.size());
9951 QCPBarData newData;
9952 for (int i=0; i<n; ++i)
9953 {
9954 newData.key = key[i];
9955 newData.value = value[i];
9956 mData->insertMulti(newData.key, newData);
9957 }
9958 }
9959
9960 /*!
9961 Moves this bars plottable below \a bars. In other words, the bars of this plottable will appear
9962 below the bars of \a bars. The move target \a bars must use the same key and value axis as this
9963 plottable.
9964
9965 Inserting into and removing from existing bar stacking is handled gracefully. If \a bars already
9966 has a bars object below itself, this bars object is inserted between the two. If this bars object
9967 is already between two other bars, the two other bars will be stacked on top of each other after
9968 the operation.
9969
9970 To remove this bars plottable from any stacking, set \a bars to 0.
9971
9972 \see moveBelow, barAbove, barBelow
9973 */
9974 void QCPBars::moveBelow(QCPBars *bars)
9975 {
9976 if (bars == this) return;
9977 if (bars->keyAxis() != mKeyAxis || bars->valueAxis() != mValueAxis)
9978 {
9979 qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
9980 return;
9981 }
9982 // remove from stacking:
9983 connectBars(mBarBelow, mBarAbove); // Note: also works if one (or both) of them is 0
9984 // if new bar given, insert this bar below it:
9985 if (bars)
9986 {
9987 if (bars->mBarBelow)
9988 connectBars(bars->mBarBelow, this);
9989 connectBars(this, bars);
9990 }
9991 }
9992
9993 /*!
9994 Moves this bars plottable above \a bars. In other words, the bars of this plottable will appear
9995 above the bars of \a bars. The move target \a bars must use the same key and value axis as this
9996 plottable.
9997
9998 Inserting into and removing from existing bar stacking is handled gracefully. If \a bars already
9999 has a bars object below itself, this bars object is inserted between the two. If this bars object
10000 is already between two other bars, the two other bars will be stacked on top of each other after
10001 the operation.
10002
10003 To remove this bars plottable from any stacking, set \a bars to 0.
10004
10005 \see moveBelow, barBelow, barAbove
10006 */
10007 void QCPBars::moveAbove(QCPBars *bars)
10008 {
10009 if (bars == this) return;
10010 if (bars && (bars->keyAxis() != mKeyAxis || bars->valueAxis() != mValueAxis))
10011 {
10012 qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
10013 return;
10014 }
10015 // remove from stacking:
10016 connectBars(mBarBelow, mBarAbove); // Note: also works if one (or both) of them is 0
10017 // if new bar given, insert this bar above it:
10018 if (bars)
10019 {
10020 if (bars->mBarAbove)
10021 connectBars(this, bars->mBarAbove);
10022 connectBars(bars, this);
10023 }
10024 }
10025
10026 /*!
10027 Adds the provided data points in \a dataMap to the current data.
10028 \see removeData
10029 */
10030 void QCPBars::addData(const QCPBarDataMap &dataMap)
10031 {
10032 mData->unite(dataMap);
10033 }
10034
10035 /*! \overload
10036 Adds the provided single data point in \a data to the current data.
10037 \see removeData
10038 */
10039 void QCPBars::addData(const QCPBarData &data)
10040 {
10041 mData->insertMulti(data.key, data);
10042 }
10043
10044 /*! \overload
10045 Adds the provided single data point as \a key and \a value tuple to the current data
10046 \see removeData
10047 */
10048 void QCPBars::addData(double key, double value)
10049 {
10050 QCPBarData newData;
10051 newData.key = key;
10052 newData.value = value;
10053 mData->insertMulti(newData.key, newData);
10054 }
10055
10056 /*! \overload
10057 Adds the provided data points as \a key and \a value tuples to the current data.
10058 \see removeData
10059 */
10060 void QCPBars::addData(const QVector<double> &keys, const QVector<double> &values)
10061 {
10062 int n = keys.size();
10063 n = qMin(n, values.size());
10064 QCPBarData newData;
10065 for (int i=0; i<n; ++i)
10066 {
10067 newData.key = keys[i];
10068 newData.value = values[i];
10069 mData->insertMulti(newData.key, newData);
10070 }
10071 }
10072
10073 /*!
10074 Removes all data points with key smaller than \a key.
10075 \see addData, clearData
10076 */
10077 void QCPBars::removeDataBefore(double key)
10078 {
10079 QCPBarDataMap::iterator it = mData->begin();
10080 while (it != mData->end() && it.key() < key)
10081 it = mData->erase(it);
10082 }
10083
10084 /*!
10085 Removes all data points with key greater than \a key.
10086 \see addData, clearData
10087 */
10088 void QCPBars::removeDataAfter(double key)
10089 {
10090 if (mData->isEmpty()) return;
10091 QCPBarDataMap::iterator it = mData->upperBound(key);
10092 while (it != mData->end())
10093 it = mData->erase(it);
10094 }
10095
10096 /*!
10097 Removes all data points with key between \a fromKey and \a toKey. if \a fromKey is
10098 greater or equal to \a toKey, the function does nothing. To remove a single data point with known
10099 key, use \ref removeData(double key).
10100
10101 \see addData, clearData
10102 */
10103 void QCPBars::removeData(double fromKey, double toKey)
10104 {
10105 if (fromKey >= toKey || mData->isEmpty()) return;
10106 QCPBarDataMap::iterator it = mData->upperBound(fromKey);
10107 QCPBarDataMap::iterator itEnd = mData->upperBound(toKey);
10108 while (it != itEnd)
10109 it = mData->erase(it);
10110 }
10111
10112 /*! \overload
10113
10114 Removes a single data point at \a key. If the position is not known with absolute precision,
10115 consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval
10116 around the suspected position, depeding on the precision with which the key is known.
10117
10118 \see addData, clearData
10119 */
10120 void QCPBars::removeData(double key)
10121 {
10122 mData->remove(key);
10123 }
10124
10125 /*!
10126 Removes all data points.
10127 \see removeData, removeDataAfter, removeDataBefore
10128 */
10129 void QCPBars::clearData()
10130 {
10131 mData->clear();
10132 }
10133
10134 /* inherits documentation from base class */
10135 double QCPBars::selectTest(const QPointF &pos) const
10136 {
10137 QCPBarDataMap::ConstIterator it;
10138 double posKey, posValue;
10139 pixelsToCoords(pos, posKey, posValue);
10140 for (it = mData->constBegin(); it != mData->constEnd(); ++it)
10141 {
10142 double baseValue = getBaseValue(it.key(), it.value().value >=0);
10143 QCPRange keyRange(it.key()-mWidth*0.5, it.key()+mWidth*0.5);
10144 QCPRange valueRange(baseValue, baseValue+it.value().value);
10145 if (keyRange.contains(posKey) && valueRange.contains(posValue))
10146 return mParentPlot->selectionTolerance()*0.99;
10147 }
10148 return -1;
10149 }
10150
10151 /* inherits documentation from base class */
10152 void QCPBars::draw(QCPPainter *painter)
10153 {
10154 if (mData->isEmpty()) return;
10155
10156 QCPBarDataMap::const_iterator it;
10157 for (it = mData->constBegin(); it != mData->constEnd(); ++it)
10158 {
10159 if (it.key()+mWidth*0.5 < mKeyAxis->range().lower || it.key()-mWidth*0.5 > mKeyAxis->range().upper)
10160 continue;
10161 QPolygonF barPolygon = getBarPolygon(it.key(), it.value().value);
10162 // draw bar fill:
10163 if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
10164 {
10165 applyFillAntialiasingHint(painter);
10166 painter->setPen(Qt::NoPen);
10167 painter->setBrush(mainBrush());
10168 painter->drawPolygon(barPolygon);
10169 }
10170 // draw bar line:
10171 if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
10172 {
10173 applyDefaultAntialiasingHint(painter);
10174 painter->setPen(mainPen());
10175 painter->setBrush(Qt::NoBrush);
10176 painter->drawPolyline(barPolygon);
10177 }
10178 }
10179 }
10180
10181 /* inherits documentation from base class */
10182 void QCPBars::drawLegendIcon(QCPPainter *painter, const QRect &rect) const
10183 {
10184 // draw filled rect:
10185 applyDefaultAntialiasingHint(painter);
10186 painter->setBrush(mBrush);
10187 painter->setPen(mPen);
10188 QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
10189 r.moveCenter(rect.center());
10190 painter->drawRect(r);
10191 }
10192
10193 /*! \internal
10194
10195 Returns the polygon of a single bar with \a key and \a value. The Polygon is open at the bottom
10196 and shifted according to the bar stacking (see \ref moveAbove).
10197 */
10198 QPolygonF QCPBars::getBarPolygon(double key, double value) const
10199 {
10200 QPolygonF result;
10201 double baseValue = getBaseValue(key, value >= 0);
10202 result << coordsToPixels(key-mWidth*0.5, baseValue);
10203 result << coordsToPixels(key-mWidth*0.5, baseValue+value);
10204 result << coordsToPixels(key+mWidth*0.5, baseValue+value);
10205 result << coordsToPixels(key+mWidth*0.5, baseValue);
10206 return result;
10207 }
10208
10209 /*! \internal
10210
10211 This function is called to find at which value to start drawing the base of a bar at \a key, when
10212 it is stacked on top of another QCPBars (e.g. with \ref moveAbove).
10213
10214 positive and negative bars are separated per stack (positive are stacked above 0-value upwards,
10215 negative are stacked below 0-value downwards). This can be indicated with \a positive. So if the
10216 bar for which we need the base value is negative, set \a positive to false.
10217 */
10218 double QCPBars::getBaseValue(double key, bool positive) const
10219 {
10220 if (mBarBelow)
10221 {
10222 double max = 0;
10223 // find bars of mBarBelow that are approximately at key and find largest one:
10224 QCPBarDataMap::const_iterator it = mBarBelow->mData->lowerBound(key-mWidth*0.1);
10225 QCPBarDataMap::const_iterator itEnd = mBarBelow->mData->upperBound(key+mWidth*0.1);
10226 while (it != itEnd)
10227 {
10228 if ((positive && it.value().value > max) ||
10229 (!positive && it.value().value < max))
10230 max = it.value().value;
10231 ++it;
10232 }
10233 // recurse down the bar-stack to find the total height:
10234 return max + mBarBelow->getBaseValue(key, positive);
10235 } else
10236 return 0;
10237 }
10238
10239 /*! \internal
10240
10241 Connects \a below and \a above to each other via their mBarAbove/mBarBelow properties.
10242 The bar(s) currently below lower and upper will become disconnected to lower/upper.
10243
10244 If lower is zero, upper will be disconnected at the bottom.
10245 If upper is zero, lower will be disconnected at the top.
10246 */
10247 void QCPBars::connectBars(QCPBars *lower, QCPBars *upper)
10248 {
10249 if (!lower && !upper) return;
10250
10251 if (!lower) // disconnect upper at bottom
10252 {
10253 // disconnect old bar below upper:
10254 if (upper->mBarBelow && upper->mBarBelow->mBarAbove == upper)
10255 upper->mBarBelow->mBarAbove = 0;
10256 upper->mBarBelow = 0;
10257 } else if (!upper) // disconnect lower at top
10258 {
10259 // disconnect old bar above lower:
10260 if (lower->mBarAbove && lower->mBarAbove->mBarBelow == lower)
10261 lower->mBarAbove->mBarBelow = 0;
10262 lower->mBarAbove = 0;
10263 } else // connect lower and upper
10264 {
10265 // disconnect old bar above lower:
10266 if (lower->mBarAbove && lower->mBarAbove->mBarBelow == lower)
10267 lower->mBarAbove->mBarBelow = 0;
10268 // disconnect old bar below upper:
10269 if (upper->mBarBelow && upper->mBarBelow->mBarAbove == upper)
10270 upper->mBarBelow->mBarAbove = 0;
10271 lower->mBarAbove = upper;
10272 upper->mBarBelow = lower;
10273 }
10274 }
10275
10276 /* inherits documentation from base class */
10277 QCPRange QCPBars::getKeyRange(bool &validRange, SignDomain inSignDomain) const
10278 {
10279 QCPRange range;
10280 bool haveLower = false;
10281 bool haveUpper = false;
10282
10283 double current;
10284 double barWidthHalf = mWidth*0.5;
10285 QCPBarDataMap::const_iterator it = mData->constBegin();
10286 while (it != mData->constEnd())
10287 {
10288 current = it.value().key;
10289 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current+barWidthHalf < 0) || (inSignDomain == sdPositive && current-barWidthHalf > 0))
10290 {
10291 if (current-barWidthHalf < range.lower || !haveLower)
10292 {
10293 range.lower = current-barWidthHalf;
10294 haveLower = true;
10295 }
10296 if (current+barWidthHalf > range.upper || !haveUpper)
10297 {
10298 range.upper = current+barWidthHalf;
10299 haveUpper = true;
10300 }
10301 }
10302 ++it;
10303 }
10304
10305 validRange = haveLower && haveUpper;
10306 return range;
10307 }
10308
10309 /* inherits documentation from base class */
10310 QCPRange QCPBars::getValueRange(bool &validRange, SignDomain inSignDomain) const
10311 {
10312 QCPRange range;
10313 bool haveLower = true; // set to true, because 0 should always be visible in bar charts
10314 bool haveUpper = true; // set to true, because 0 should always be visible in bar charts
10315
10316 double current;
10317
10318 QCPBarDataMap::const_iterator it = mData->constBegin();
10319 while (it != mData->constEnd())
10320 {
10321 current = it.value().value + getBaseValue(it.value().key, it.value().value >= 0);
10322 if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
10323 {
10324 if (current < range.lower || !haveLower)
10325 {
10326 range.lower = current;
10327 haveLower = true;
10328 }
10329 if (current > range.upper || !haveUpper)
10330 {
10331 range.upper = current;
10332 haveUpper = true;
10333 }
10334 }
10335 ++it;
10336 }
10337
10338 validRange = range.lower < range.upper;
10339 return range;
10340 }
10341
10342
10343 // ================================================================================
10344 // =================== QCPStatisticalBox
10345 // ================================================================================
10346
10347 /*! \class QCPStatisticalBox
10348 \brief A plottable representing a single statistical box in a plot.
10349
10350 To plot data, assign it with the individual parameter functions or use \ref setData to set all
10351 parameters at once. The individual funcions are:
10352 \li \ref setMinimum
10353 \li \ref setLowerQuartile
10354 \li \ref setMedian
10355 \li \ref setUpperQuartile
10356 \li \ref setMaximum
10357
10358 Additionally you can define a list of outliers, drawn as circle datapoints:
10359 \li \ref setOutliers
10360
10361 \section appearance Changing the appearance
10362
10363 The appearance of the box itself is controlled via \ref setPen and \ref setBrush. You
10364 may change the width of the box with \ref setWidth in plot coordinates (not pixels).
10365
10366 Analog functions exist for the minimum/maximum-whiskers: \ref setWhiskerPen, \ref
10367 setWhiskerBarPen, \ref setWhiskerWidth. The whisker width is the width of the bar at the top
10368 (maximum) or bottom (minimum).
10369
10370 The median indicator line has its own pen, \ref setMedianPen.
10371
10372 If the pens are changed, especially the whisker pen, make sure to set the capStyle to
10373 Qt::FlatCap. Else, e.g. the whisker line might exceed the bar line by a few pixels due to the pen
10374 cap being not perfectly flat.
10375
10376 The Outlier data points are drawn normal scatter points. Their look can be controlled with \ref
10377 setOutlierStyle and \ref setOutlierPen. The size (diameter) can be set with \ref setOutlierSize
10378 in pixels.
10379
10380 \section usage Usage
10381
10382 Like all data representing objects in QCustomPlot, the QCPStatisticalBox is a plottable
10383 (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies
10384 (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
10385
10386 Usually, you first create an instance:
10387 \code
10388 QCPStatisticalBox *newBox = new QCPStatisticalBox(customPlot->xAxis, customPlot->yAxis);\endcode
10389 add it to the customPlot with QCustomPlot::addPlottable:
10390 \code
10391 customPlot->addPlottable(newBox);\endcode
10392 and then modify the properties of the newly created plottable, e.g.:
10393 \code
10394 newBox->setName("Measurement Series 1");
10395 newBox->setData(1, 3, 4, 5, 7);
10396 newBox->setOutliers(QVector<double>() << 0.5 << 0.64 << 7.2 << 7.42);\endcode
10397 */
10398
10399 /*!
10400 Constructs a statistical box which uses \a keyAxis as its key axis ("x") and \a valueAxis as its
10401 value axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and
10402 not have the same orientation. If either of these restrictions is violated, a corresponding
10403 message is printed to the debug output (qDebug), the construction is not aborted, though.
10404
10405 The constructed statistical box can be added to the plot with QCustomPlot::addPlottable,
10406 QCustomPlot then takes ownership of the statistical box.
10407 */
10408 QCPStatisticalBox::QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis) :
10409 QCPAbstractPlottable(keyAxis, valueAxis),
10410 mKey(0),
10411 mMinimum(0),
10412 mLowerQuartile(0),
10413 mMedian(0),
10414 mUpperQuartile(0),
10415 mMaximum(0)
10416 {
10417 setOutlierStyle(QCP::ssCircle);
10418 setOutlierSize(5);
10419 setWhiskerWidth(0.2);
10420 setWidth(0.5);
10421
10422 setPen(QPen(Qt::black));
10423 setSelectedPen(QPen(Qt::blue, 2.5));
10424 setMedianPen(QPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap));
10425 setWhiskerPen(QPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap));
10426 setWhiskerBarPen(QPen(Qt::black));
10427 setOutlierPen(QPen(Qt::blue));
10428 setBrush(Qt::NoBrush);
10429 setSelectedBrush(Qt::NoBrush);
10430 }
10431
10432 QCPStatisticalBox::~QCPStatisticalBox()
10433 {
10434 }
10435
10436 /*!
10437 Sets the key coordinate of the statistical box.
10438 */
10439 void QCPStatisticalBox::setKey(double key)
10440 {
10441 mKey = key;
10442 }
10443
10444 /*!
10445 Sets the parameter "minimum" of the statistical box plot. This is the position of the lower
10446 whisker, typically the minimum measurement of the sample that's not considered an outlier.
10447
10448 \see setMaximum, setWhiskerPen, setWhiskerBarPen, setWhiskerWidth
10449 */
10450 void QCPStatisticalBox::setMinimum(double value)
10451 {
10452 mMinimum = value;
10453 }
10454
10455 /*!
10456 Sets the parameter "lower Quartile" of the statistical box plot. This is the lower end of the
10457 box. The lower and the upper quartiles are the two statistical quartiles around the median of the
10458 sample, they contain 50% of the sample data.
10459
10460 \see setUpperQuartile, setPen, setBrush, setWidth
10461 */
10462 void QCPStatisticalBox::setLowerQuartile(double value)
10463 {
10464 mLowerQuartile = value;
10465 }
10466
10467 /*!
10468 Sets the parameter "median" of the statistical box plot. This is the value of the median mark
10469 inside the quartile box. The median separates the sample data in half (50% of the sample data is
10470 below/above the median).
10471
10472 \see setMedianPen
10473 */
10474 void QCPStatisticalBox::setMedian(double value)
10475 {
10476 mMedian = value;
10477 }
10478
10479 /*!
10480 Sets the parameter "upper Quartile" of the statistical box plot. This is the upper end of the
10481 box. The lower and the upper quartiles are the two statistical quartiles around the median of the
10482 sample, they contain 50% of the sample data.
10483
10484 \see setLowerQuartile, setPen, setBrush, setWidth
10485 */
10486 void QCPStatisticalBox::setUpperQuartile(double value)
10487 {
10488 mUpperQuartile = value;
10489 }
10490
10491 /*!
10492 Sets the parameter "maximum" of the statistical box plot. This is the position of the upper
10493 whisker, typically the maximum measurement of the sample that's not considered an outlier.
10494
10495 \see setMinimum, setWhiskerPen, setWhiskerBarPen, setWhiskerWidth
10496 */
10497 void QCPStatisticalBox::setMaximum(double value)
10498 {
10499 mMaximum = value;
10500 }
10501
10502 /*!
10503 Sets a vector of outlier values that will be drawn as circles. Any data points in the sample that
10504 are not within the whiskers (\ref setMinimum, \ref setMaximum) should be considered outliers and
10505 displayed as such.
10506
10507 \see setOutlierPen, setOutlierBrush, setOutlierSize
10508 */
10509 void QCPStatisticalBox::setOutliers(const QVector<double> &values)
10510 {
10511 mOutliers = values;
10512 }
10513
10514 /*!
10515 Sets all parameters of the statistical box plot at once.
10516
10517 \see setKey, setMinimum, setLowerQuartile, setMedian, setUpperQuartile, setMaximum
10518 */
10519 void QCPStatisticalBox::setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum)
10520 {
10521 setKey(key);
10522 setMinimum(minimum);
10523 setLowerQuartile(lowerQuartile);
10524 setMedian(median);
10525 setUpperQuartile(upperQuartile);
10526 setMaximum(maximum);
10527 }
10528
10529 /*!
10530 Sets the width of the box in key coordinates.
10531
10532 \see setWhiskerWidth
10533 */
10534 void QCPStatisticalBox::setWidth(double width)
10535 {
10536 mWidth = width;
10537 }
10538
10539 /*!
10540 Sets the width of the whiskers (\ref setMinimum, \ref setMaximum) in key coordinates.
10541
10542 \see setWidth
10543 */
10544 void QCPStatisticalBox::setWhiskerWidth(double width)
10545 {
10546 mWhiskerWidth = width;
10547 }
10548
10549 /*!
10550 Sets the pen used for drawing the whisker backbone (That's the line parallel to the value axis).
10551
10552 Make sure to set the \a pen capStyle to Qt::FlatCap to prevent the backbone from reaching a few
10553 pixels past the bars, when using a non-zero pen width.
10554
10555 \see setWhiskerBarPen
10556 */
10557 void QCPStatisticalBox::setWhiskerPen(const QPen &pen)
10558 {
10559 mWhiskerPen = pen;
10560 }
10561
10562 /*!
10563 Sets the pen used for drawing the whisker bars (Those are the lines parallel to the key axis at
10564 each end of the backbone).
10565
10566 \see setWhiskerPen
10567 */
10568 void QCPStatisticalBox::setWhiskerBarPen(const QPen &pen)
10569 {
10570 mWhiskerBarPen = pen;
10571 }
10572
10573 /*!
10574 Sets the pen used for drawing the median indicator line inside the statistical box.
10575
10576 Make sure to set the \a pen capStyle to Qt::FlatCap to prevent the median line from reaching a
10577 few pixels outside the box, when using a non-zero pen width.
10578 */
10579 void QCPStatisticalBox::setMedianPen(const QPen &pen)
10580 {
10581 mMedianPen = pen;
10582 }
10583
10584 /*!
10585 Sets the pixel size of the scatter symbols that represent the outlier data points.
10586
10587 \see setOutlierPen, setOutliers
10588 */
10589 void QCPStatisticalBox::setOutlierSize(double pixels)
10590 {
10591 mOutlierSize = pixels;
10592 }
10593
10594 /*!
10595 Sets the pen used to draw the outlier data points.
10596
10597 \see setOutlierSize, setOutliers
10598 */
10599 void QCPStatisticalBox::setOutlierPen(const QPen &pen)
10600 {
10601 mOutlierPen = pen;
10602 }
10603
10604 /*!
10605 Sets the scatter style of the outlier data points.
10606
10607 \see setOutlierSize, setOutlierPen, setOutliers
10608 */
10609 void QCPStatisticalBox::setOutlierStyle(QCP::ScatterStyle style)
10610 {
10611 mOutlierStyle = style;
10612 }
10613
10614 /* inherits documentation from base class */
10615 void QCPStatisticalBox::clearData()
10616 {
10617 setOutliers(QVector<double>());
10618 setKey(0);
10619 setMinimum(0);
10620 setLowerQuartile(0);
10621 setMedian(0);
10622 setUpperQuartile(0);
10623 setMaximum(0);
10624 }
10625
10626 /* inherits documentation from base class */
10627 double QCPStatisticalBox::selectTest(const QPointF &pos) const
10628 {
10629 double posKey, posValue;
10630 pixelsToCoords(pos, posKey, posValue);
10631 // quartile box:
10632 QCPRange keyRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
10633 QCPRange valueRange(mLowerQuartile, mUpperQuartile);
10634 if (keyRange.contains(posKey) && valueRange.contains(posValue))
10635 return mParentPlot->selectionTolerance()*0.99;
10636
10637 // min/max whiskers:
10638 if (QCPRange(mMinimum, mMaximum).contains(posValue))
10639 return qAbs(mKeyAxis->coordToPixel(mKey)-mKeyAxis->coordToPixel(posKey));
10640
10641 return -1;
10642 }
10643
10644 /* inherits documentation from base class */
10645 void QCPStatisticalBox::draw(QCPPainter *painter)
10646 {
10647 QRectF quartileBox;
10648 drawQuartileBox(painter, &quartileBox);
10649
10650 painter->save();
10651 painter->setClipRect(quartileBox, Qt::IntersectClip);
10652 drawMedian(painter);
10653 painter->restore();
10654
10655 drawWhiskers(painter);
10656 drawOutliers(painter);
10657 }
10658
10659 /* inherits documentation from base class */
10660 void QCPStatisticalBox::drawLegendIcon(QCPPainter *painter, const QRect &rect) const
10661 {
10662 // draw filled rect:
10663 applyDefaultAntialiasingHint(painter);
10664 painter->setPen(mPen);
10665 painter->setBrush(mBrush);
10666 QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
10667 r.moveCenter(rect.center());
10668 painter->drawRect(r);
10669 }
10670
10671 /*! \internal
10672
10673 Draws the quartile box. \a box is an output parameter that returns the quartile box (in pixel
10674 coordinates) which is used to set the clip rect of the painter before calling \ref drawMedian (so
10675 the median doesn't draw outside the quartile box).
10676 */
10677 void QCPStatisticalBox::drawQuartileBox(QCPPainter *painter, QRectF *quartileBox) const
10678 {
10679 QRectF box;
10680 box.setTopLeft(coordsToPixels(mKey-mWidth*0.5, mUpperQuartile));
10681 box.setBottomRight(coordsToPixels(mKey+mWidth*0.5, mLowerQuartile));
10682 applyDefaultAntialiasingHint(painter);
10683 painter->setPen(mainPen());
10684 painter->setBrush(mainBrush());
10685 painter->drawRect(box);
10686 if (quartileBox)
10687 *quartileBox = box;
10688 }
10689
10690 /*! \internal
10691
10692 Draws the median line inside the quartile box.
10693 */
10694 void QCPStatisticalBox::drawMedian(QCPPainter *painter) const
10695 {
10696 QLineF medianLine;
10697 medianLine.setP1(coordsToPixels(mKey-mWidth*0.5, mMedian));
10698 medianLine.setP2(coordsToPixels(mKey+mWidth*0.5, mMedian));
10699 applyDefaultAntialiasingHint(painter);
10700 painter->setPen(mMedianPen);
10701 painter->drawLine(medianLine);
10702 }
10703
10704 /*! \internal
10705
10706 Draws both whisker backbones and bars.
10707 */
10708 void QCPStatisticalBox::drawWhiskers(QCPPainter *painter) const
10709 {
10710 QLineF backboneMin, backboneMax, barMin, barMax;
10711 backboneMax.setPoints(coordsToPixels(mKey, mUpperQuartile), coordsToPixels(mKey, mMaximum));
10712 backboneMin.setPoints(coordsToPixels(mKey, mLowerQuartile), coordsToPixels(mKey, mMinimum));
10713 barMax.setPoints(coordsToPixels(mKey-mWhiskerWidth*0.5, mMaximum), coordsToPixels(mKey+mWhiskerWidth*0.5, mMaximum));
10714 barMin.setPoints(coordsToPixels(mKey-mWhiskerWidth*0.5, mMinimum), coordsToPixels(mKey+mWhiskerWidth*0.5, mMinimum));
10715 applyErrorBarsAntialiasingHint(painter);
10716 painter->setPen(mWhiskerPen);
10717 painter->drawLine(backboneMin);
10718 painter->drawLine(backboneMax);
10719 painter->setPen(mWhiskerBarPen);
10720 painter->drawLine(barMin);
10721 painter->drawLine(barMax);
10722 }
10723
10724 /*! \internal
10725
10726 Draws the outlier circles.
10727 */
10728 void QCPStatisticalBox::drawOutliers(QCPPainter *painter) const
10729 {
10730 applyScattersAntialiasingHint(painter);
10731 painter->setPen(mOutlierPen);
10732 painter->setBrush(Qt::NoBrush);
10733 for (int i=0; i<mOutliers.size(); ++i)
10734 {
10735 QPointF dataPoint = coordsToPixels(mKey, mOutliers.at(i));
10736 painter->drawScatter(dataPoint.x(), dataPoint.y(), mOutlierSize, mOutlierStyle);
10737 }
10738 }
10739
10740 /* inherits documentation from base class */
10741 QCPRange QCPStatisticalBox::getKeyRange(bool &validRange, SignDomain inSignDomain) const
10742 {
10743 validRange = mWidth > 0;
10744 if (inSignDomain == sdBoth)
10745 {
10746 return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
10747 } else if (inSignDomain == sdNegative)
10748 {
10749 if (mKey+mWidth*0.5 < 0)
10750 return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
10751 else if (mKey < 0)
10752 return QCPRange(mKey-mWidth*0.5, mKey);
10753 else
10754 {
10755 validRange = false;
10756 return QCPRange();
10757 }
10758 } else if (inSignDomain == sdPositive)
10759 {
10760 if (mKey-mWidth*0.5 > 0)
10761 return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
10762 else if (mKey > 0)
10763 return QCPRange(mKey, mKey+mWidth*0.5);
10764 else
10765 {
10766 validRange = false;
10767 return QCPRange();
10768 }
10769 }
10770 validRange = false;
10771 return QCPRange();
10772 }
10773
10774 /* inherits documentation from base class */
10775 QCPRange QCPStatisticalBox::getValueRange(bool &validRange, SignDomain inSignDomain) const
10776 {
10777 if (inSignDomain == sdBoth)
10778 {
10779 double lower = qMin(mMinimum, qMin(mMedian, mLowerQuartile));
10780 double upper = qMax(mMaximum, qMax(mMedian, mUpperQuartile));
10781 for (int i=0; i<mOutliers.size(); ++i)
10782 {
10783 if (mOutliers.at(i) < lower)
10784 lower = mOutliers.at(i);
10785 if (mOutliers.at(i) > upper)
10786 upper = mOutliers.at(i);
10787 }
10788 validRange = upper > lower;
10789 return QCPRange(lower, upper);
10790 } else
10791 {
10792 QVector<double> values; // values that must be considered (i.e. all outliers and the five box-parameters)
10793 values.reserve(mOutliers.size() + 5);
10794 values << mMaximum << mUpperQuartile << mMedian << mLowerQuartile << mMinimum;
10795 values << mOutliers;
10796 // go through values and find the ones in legal range:
10797 bool haveUpper = false;
10798 bool haveLower = false;
10799 double upper = 0;
10800 double lower = 0;
10801 for (int i=0; i<values.size(); ++i)
10802 {
10803 if ((inSignDomain == sdNegative && values.at(i) < 0) ||
10804 (inSignDomain == sdPositive && values.at(i) > 0))
10805 {
10806 if (values.at(i) > upper || !haveUpper)
10807 {
10808 upper = values.at(i);
10809 haveUpper = true;
10810 }
10811 if (values.at(i) < lower || !haveLower)
10812 {
10813 lower = values.at(i);
10814 haveLower = true;
10815 }
10816 }
10817 }
10818 // return the bounds if we found some sensible values:
10819 if (haveLower && haveUpper && lower < upper)
10820 {
10821 validRange = true;
10822 return QCPRange(lower, upper);
10823 } else
10824 {
10825 validRange = false;
10826 return QCPRange();
10827 }
10828 }
10829 }
10830
10831
10832 // ================================================================================
10833 // =================== QCPAbstractItem
10834 // ================================================================================
10835
10836 /*! \class QCPAbstractItem
10837 \brief The abstract base class for all items in a plot.
10838
10839 In QCustomPlot, items are supplemental graphical elements that are neither plottables
10840 (QCPAbstractPlottable) nor axes (QCPAxis). While plottables are always tied to two axes and thus
10841 plot coordinates, items can also be placed in absolute coordinates independent of any axes. Each
10842 specific item has at least one QCPItemPosition member which controls the positioning. Some items
10843 are defined by more than one coordinate and thus have two or more QCPItemPosition members (For
10844 example, QCPItemRect has \a topLeft and \a bottomRight).
10845
10846 This abstract base class defines a very basic interface like visibility and clipping. Since this
10847 class is abstract, it can't be instantiated. Use one of the subclasses or create a subclass
10848 yourself to create new items.
10849
10850 The built-in items are:
10851 <table>
10852 <tr><td>QCPItemLine</td><td>A line defined by a start and an end point. May have different ending styles on each side (e.g. arrows).</td></tr>
10853 <tr><td>QCPItemStraightLine</td><td>A straight line defined by a start and a direction point. Unlike QCPItemLine, the straight line is infinitely long and has no endings.</td></tr>
10854 <tr><td>QCPItemCurve</td><td>A curve defined by start, end and two intermediate control points. May have different ending styles on each side (e.g. arrows).</td></tr>
10855 <tr><td>QCPItemRect</td><td>A rectangle</td></tr>
10856 <tr><td>QCPItemEllipse</td><td>An ellipse</td></tr>
10857 <tr><td>QCPItemPixmap</td><td>An arbitrary pixmap</td></tr>
10858 <tr><td>QCPItemText</td><td>A text label</td></tr>
10859 <tr><td>QCPItemBracket</td><td>A bracket which may be used to reference/highlight certain parts in the plot.</td></tr>
10860 <tr><td>QCPItemTracer</td><td>An item that can be attached to a QCPGraph and sticks to its data points, given a key coordinate.</td></tr>
10861 </table>
10862
10863 \section items-using Using items
10864
10865 First you instantiate the item you want to use and add it to the plot:
10866 \code
10867 QCPItemLine *line = new QCPItemLine(customPlot);
10868 customPlot->addItem(line);
10869 \endcode
10870 by default, the positions of the item are bound to the x- and y-Axis of the plot. So we can just
10871 set the plot coordinates where the line should start/end:
10872 \code
10873 line->start->setCoords(-0.1, 0.8);
10874 line->end->setCoords(1.1, 0.2);
10875 \endcode
10876 If we wanted the line to be positioned not in plot coordinates but a different coordinate system,
10877 e.g. absolute pixel positions on the QCustomPlot surface, we would have changed the position type
10878 like this:
10879 \code
10880 line->start->setType(QCPItemPosition::ptAbsolute);
10881 line->end->setType(QCPItemPosition::ptAbsolute);
10882 \endcode
10883 Then we can set the coordinates, this time in pixels:
10884 \code
10885 line->start->setCoords(100, 200);
10886 line->end->setCoords(450, 320);
10887 \endcode
10888
10889 \section items-subclassing Creating own items
10890
10891 To create an own item, you implement a subclass of QCPAbstractItem. These are the pure
10892 virtual functions, you must implement:
10893 \li \ref selectTest
10894 \li \ref draw
10895
10896 See the documentation of those functions for what they need to do.
10897
10898 \subsection items-positioning Allowing the item to be positioned
10899
10900 As mentioned, item positions are represented by QCPItemPosition members. Let's assume the new item shall
10901 have only one coordinate as its position (as opposed to two like a rect or multiple like a polygon). You then add
10902 a public member of type QCPItemPosition like so:
10903
10904 \code QCPItemPosition * const myPosition;\endcode
10905
10906 the const makes sure the pointer itself can't be modified from the user of your new item (the QCPItemPosition
10907 instance it points to, can be modified, of course).
10908 The initialization of this pointer is made easy with the \ref createPosition function. Just assign
10909 the return value of this function to each QCPItemPosition in the constructor of your item. \ref createPosition
10910 takes a string which is the name of the position, typically this is identical to the variable name.
10911 For example, the constructor of QCPItemExample could look like this:
10912
10913 \code
10914 QCPItemExample::QCPItemExample(QCustomPlot *parentPlot) :
10915 QCPAbstractItem(parentPlot),
10916 myPosition(createPosition("myPosition"))
10917 {
10918 // other constructor code
10919 }
10920 \endcode
10921
10922 \subsection items-drawing The draw function
10923
10924 Your implementation of the draw function should check whether the item is visible (\a mVisible)
10925 and then draw the item. You can retrieve its position in pixel coordinates from the position
10926 member(s) via \ref QCPItemPosition::pixelPoint.
10927
10928 To optimize performance you should calculate a bounding rect first (don't forget to take the pen
10929 width into account), check whether it intersects the \ref clipRect, and only draw the item at all
10930 if this is the case.
10931
10932 \subsection items-selection The selectTest function
10933
10934 Your implementation of the \ref selectTest function may use the helpers \ref distSqrToLine and
10935 \ref rectSelectTest. With these, the implementation of the selection test becomes significantly
10936 simpler for most items.
10937
10938 \subsection anchors Providing anchors
10939
10940 Providing anchors (QCPItemAnchor) starts off like adding a position. First you create a public
10941 member, e.g.
10942
10943 \code QCPItemAnchor * const bottom;\endcode
10944
10945 and create it in the constructor with the \ref createAnchor function, assigning it a name and an
10946 anchor id (an integer enumerating all anchors on the item, you may create an own enum for this).
10947 Since anchors can be placed anywhere, relative to the item's position(s), your item needs to
10948 provide the position of every anchor with the reimplementation of the \ref anchorPixelPoint(int
10949 anchorId) function.
10950
10951 In essence the QCPItemAnchor is merely an intermediary that itself asks your item for the pixel
10952 position when anything attached to the anchor needs to know the coordinates.
10953 */
10954
10955 /* start of documentation of inline functions */
10956
10957 /*! \fn QList<QCPItemPosition*> QCPAbstractItem::positions() const
10958
10959 Returns all positions of the item in a list.
10960
10961 \see anchors, position
10962 */
10963
10964 /*! \fn QList<QCPItemAnchor*> QCPAbstractItem::anchors() const
10965
10966 Returns all anchors of the item in a list. Note that since a position (QCPItemPosition) is always
10967 also an anchor, the list will also contain the positions of this item.
10968
10969 \see positions, anchor
10970 */
10971
10972 /* end of documentation of inline functions */
10973 /* start documentation of pure virtual functions */
10974
10975 /*! \fn double QCPAbstractItem::selectTest(const QPointF &pos) const = 0
10976
10977 This function is used to decide whether a click hits an item or not.
10978
10979 \a pos is a point in pixel coordinates on the QCustomPlot surface. This function returns the
10980 shortest pixel distance of this point to the item. If the item is either invisible or the
10981 distance couldn't be determined, -1.0 is returned. \ref setSelectable has no influence on the
10982 return value of this function.
10983
10984 If the item is represented not by single lines but by an area like QCPItemRect or QCPItemText, a
10985 click inside the area returns a constant value greater zero (typically 99% of the
10986 selectionTolerance of the parent QCustomPlot). If the click lies outside the area, this function
10987 returns -1.0.
10988
10989 Providing a constant value for area objects allows selecting line objects even when they are
10990 obscured by such area objects, by clicking close to the lines (i.e. closer than
10991 0.99*selectionTolerance).
10992
10993 The actual setting of the selection state is not done by this function. This is handled by the
10994 parent QCustomPlot when the mouseReleaseEvent occurs.
10995
10996 \see setSelected, QCustomPlot::setInteractions
10997 */
10998
10999 /*! \fn void QCPAbstractItem::draw(QCPPainter *painter) = 0
11000 \internal
11001
11002 Draws this item with the provided \a painter. Called by \ref QCustomPlot::draw on all its
11003 visible items.
11004
11005 The cliprect of the provided painter is set to the rect returned by \ref clipRect before this
11006 function is called. For items this depends on the clipping settings defined by \ref
11007 setClipToAxisRect, \ref setClipKeyAxis and \ref setClipValueAxis.
11008 */
11009
11010 /* end documentation of pure virtual functions */
11011 /* start documentation of signals */
11012
11013 /*! \fn void QCPAbstractItem::selectionChanged(bool selected)
11014 This signal is emitted when the selection state of this item has changed, either by user interaction
11015 or by a direct call to \ref setSelected.
11016 */
11017
11018 /* end documentation of signals */
11019
11020 /*!
11021 Base class constructor which initializes base class members.
11022 */
11023 QCPAbstractItem::QCPAbstractItem(QCustomPlot *parentPlot) :
11024 QCPLayerable(parentPlot),
11025 mClipToAxisRect(true),
11026 mClipKeyAxis(parentPlot->xAxis),
11027 mClipValueAxis(parentPlot->yAxis),
11028 mSelectable(true),
11029 mSelected(false)
11030 {
11031 }
11032
11033 QCPAbstractItem::~QCPAbstractItem()
11034 {
11035 // don't delete mPositions because every position is also an anchor and thus in mAnchors
11036 qDeleteAll(mAnchors);
11037 }
11038
11039 /*!
11040 Sets whether the item shall be clipped to the axis rect or whether it shall be visible on the
11041 entire QCustomPlot. The axis rect is defined by the clip axes which can be set via \ref
11042 setClipAxes or individually with \ref setClipKeyAxis and \ref setClipValueAxis.
11043 */
11044 void QCPAbstractItem::setClipToAxisRect(bool clip)
11045 {
11046 mClipToAxisRect = clip;
11047 }
11048
11049 /*!
11050 Sets both clip axes. Together they define the axis rect that will be used to clip the item
11051 when \ref setClipToAxisRect is set to true.
11052
11053 \see setClipToAxisRect, setClipKeyAxis, setClipValueAxis
11054 */
11055 void QCPAbstractItem::setClipAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
11056 {
11057 mClipKeyAxis = keyAxis;
11058 mClipValueAxis = valueAxis;
11059 }
11060
11061 /*!
11062 Sets the clip key axis. Together with the clip value axis it defines the axis rect that will be
11063 used to clip the item when \ref setClipToAxisRect is set to true.
11064
11065 \see setClipToAxisRect, setClipAxes, setClipValueAxis
11066 */
11067 void QCPAbstractItem::setClipKeyAxis(QCPAxis *axis)
11068 {
11069 mClipKeyAxis = axis;
11070 }
11071
11072 /*!
11073 Sets the clip value axis. Together with the clip key axis it defines the axis rect that will be
11074 used to clip the item when \ref setClipToAxisRect is set to true.
11075
11076 \see setClipToAxisRect, setClipAxes, setClipKeyAxis
11077 */
11078 void QCPAbstractItem::setClipValueAxis(QCPAxis *axis)
11079 {
11080 mClipValueAxis = axis;
11081 }
11082
11083 /*!
11084 Sets whether the user can (de-)select this item by clicking on the QCustomPlot surface.
11085 (When \ref QCustomPlot::setInteractions contains QCustomPlot::iSelectItems.)
11086
11087 However, even when \a selectable was set to false, it is possible to set the selection manually,
11088 by calling \ref setSelected directly.
11089
11090 \see QCustomPlot::setInteractions, setSelected
11091 */
11092 void QCPAbstractItem::setSelectable(bool selectable)
11093 {
11094 mSelectable = selectable;
11095 }
11096
11097 /*!
11098 Sets whether this item is selected or not. When selected, it might use a different visual
11099 appearance (e.g. pen and brush), this depends on the specific item, though.
11100
11101 The entire selection mechanism for items is handled automatically when \ref
11102 QCustomPlot::setInteractions contains QCustomPlot::iSelectItems. You only need to call this function when you
11103 wish to change the selection state manually.
11104
11105 This function can change the selection state even when \ref setSelectable was set to false.
11106
11107 emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
11108
11109 \see selectTest
11110 */
11111 void QCPAbstractItem::setSelected(bool selected)
11112 {
11113 if (mSelected != selected)
11114 {
11115 mSelected = selected;
11116 emit selectionChanged(mSelected);
11117 }
11118 }
11119
11120 /*!
11121 Returns the QCPItemPosition with the specified \a name. If this item doesn't have a position by
11122 that name, returns 0.
11123
11124 This function provides an alternative way to access item positions. Normally, you access
11125 positions direcly by their member pointers (which typically have the same variable name as \a
11126 name).
11127
11128 \see positions, anchor
11129 */
11130 QCPItemPosition *QCPAbstractItem::position(const QString &name) const
11131 {
11132 for (int i=0; i<mPositions.size(); ++i)
11133 {
11134 if (mPositions.at(i)->name() == name)
11135 return mPositions.at(i);
11136 }
11137 qDebug() << Q_FUNC_INFO << "position with name not found:" << name;
11138 return 0;
11139 }
11140
11141 /*!
11142 Returns the QCPItemAnchor with the specified \a name. If this item doesn't have an anchor by
11143 that name, returns 0.
11144
11145 This function provides an alternative way to access item anchors. Normally, you access
11146 anchors direcly by their member pointers (which typically have the same variable name as \a
11147 name).
11148
11149 \see anchors, position
11150 */
11151 QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const
11152 {
11153 for (int i=0; i<mAnchors.size(); ++i)
11154 {
11155 if (mAnchors.at(i)->name() == name)
11156 return mAnchors.at(i);
11157 }
11158 qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name;
11159 return 0;
11160 }
11161
11162 /*!
11163 Returns whether this item has an anchor with the specified \a name.
11164
11165 Note that you can check for positions with this function, too, because every position is also an
11166 anchor (QCPItemPosition inherits from QCPItemAnchor).
11167
11168 \see anchor, position
11169 */
11170 bool QCPAbstractItem::hasAnchor(const QString &name) const
11171 {
11172 for (int i=0; i<mAnchors.size(); ++i)
11173 {
11174 if (mAnchors.at(i)->name() == name)
11175 return true;
11176 }
11177 return false;
11178 }
11179
11180 /*! \internal
11181
11182 Returns the rect the visual representation of this item is clipped to. This depends on the
11183 current setting of \ref setClipToAxisRect aswell as the clip axes set with \ref setClipAxes.
11184
11185 If the item is not clipped to an axis rect, the \ref QCustomPlot::viewport rect is returned.
11186
11187 \see draw
11188 */
11189 QRect QCPAbstractItem::clipRect() const
11190 {
11191 if (mClipToAxisRect)
11192 {
11193 if (mClipKeyAxis && mClipValueAxis)
11194 return mClipKeyAxis->axisRect() | mClipValueAxis->axisRect();
11195 else if (mClipKeyAxis)
11196 return mClipKeyAxis->axisRect();
11197 else if (mClipValueAxis)
11198 return mClipValueAxis->axisRect();
11199 }
11200
11201 return mParentPlot->viewport();
11202 }
11203
11204 /*! \internal
11205
11206 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
11207 before drawing item lines.
11208
11209 This is the antialiasing state the painter passed to the \ref draw method is in by default.
11210
11211 This function takes into account the local setting of the antialiasing flag as well as
11212 the overrides set e.g. with \ref QCustomPlot::setNotAntialiasedElements.
11213
11214 \see setAntialiased
11215 */
11216 void QCPAbstractItem::applyDefaultAntialiasingHint(QCPPainter *painter) const
11217 {
11218 applyAntialiasingHint(painter, mAntialiased, QCP::aeItems);
11219 }
11220
11221 /*! \internal
11222
11223 Finds the shortest squared distance of \a point to the line segment defined by \a start and \a
11224 end.
11225
11226 This function may be used to help with the implementation of the \ref selectTest function for
11227 specific items.
11228
11229 \note This function is identical to QCPAbstractPlottable::distSqrToLine
11230
11231 \see rectSelectTest
11232 */
11233 double QCPAbstractItem::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
11234 {
11235 QVector2D a(start);
11236 QVector2D b(end);
11237 QVector2D p(point);
11238 QVector2D v(b-a);
11239
11240 double vLengthSqr = v.lengthSquared();
11241 if (!qFuzzyIsNull(vLengthSqr))
11242 {
11243 double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
11244 if (mu < 0)
11245 return (a-p).lengthSquared();
11246 else if (mu > 1)
11247 return (b-p).lengthSquared();
11248 else
11249 return ((a + mu*v)-p).lengthSquared();
11250 } else
11251 return (a-p).lengthSquared();
11252 }
11253
11254 /*! \internal
11255
11256 A convenience function which returns the selectTest value for a specified \a rect and a specified
11257 click position \a pos. \a filledRect defines whether a click inside the rect should also be
11258 considered a hit or whether only the rect border is sensitive to hits.
11259
11260 This function may be used to help with the implementation of the \ref selectTest function for
11261 specific items.
11262
11263 For example, if your item consists of four rects, call this function four times, once for each
11264 rect, in your \ref selectTest reimplementation. Finally, return the minimum of all four returned
11265 values which were greater or equal to zero. (Because this function may return -1.0 when \a pos
11266 doesn't hit \a rect at all). If all calls returned -1.0, return -1.0, too, because your item
11267 wasn't hit.
11268
11269 \see distSqrToLine
11270 */
11271 double QCPAbstractItem::rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const
11272 {
11273 double result = -1;
11274
11275 // distance to border:
11276 QList<QLineF> lines;
11277 lines << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight())
11278 << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight());
11279 double minDistSqr = std::numeric_limits<double>::max();
11280 for (int i=0; i<lines.size(); ++i)
11281 {
11282 double distSqr = distSqrToLine(lines.at(i).p1(), lines.at(i).p2(), pos);
11283 if (distSqr < minDistSqr)
11284 minDistSqr = distSqr;
11285 }
11286 result = qSqrt(minDistSqr);
11287
11288 // filled rect, allow click inside to count as hit:
11289 if (filledRect && result > mParentPlot->selectionTolerance()*0.99)
11290 {
11291 if (rect.contains(pos))
11292 result = mParentPlot->selectionTolerance()*0.99;
11293 }
11294 return result;
11295 }
11296
11297 /*! \internal
11298
11299 Returns the pixel position of the anchor with Id \a anchorId. This function must be reimplemented in
11300 item subclasses if they want to provide anchors (QCPItemAnchor).
11301
11302 For example, if the item has two anchors with id 0 and 1, this function takes one of these anchor
11303 ids and returns the respective pixel points of the specified anchor.
11304
11305 \see createAnchor
11306 */
11307 QPointF QCPAbstractItem::anchorPixelPoint(int anchorId) const
11308 {
11309 qDebug() << Q_FUNC_INFO << "called on item which shouldn't have any anchors (anchorPixelPos not reimplemented). anchorId" << anchorId;
11310 return QPointF();
11311 }
11312
11313 /*! \internal
11314
11315 Creates a QCPItemPosition, registers it with this item and returns a pointer to it. The specified
11316 \a name must be a unique string that is usually identical to the variable name of the position
11317 member (This is needed to provide the name based \ref position access to positions).
11318
11319 Don't delete positions created by this function manually, as the item will take care of it.
11320
11321 Use this function in the constructor (initialization list) of the specific item subclass to
11322 create each position member. Don't create QCPItemPositions with \b new yourself, because they
11323 won't be registered with the item properly.
11324
11325 \see createAnchor
11326 */
11327 QCPItemPosition *QCPAbstractItem::createPosition(const QString &name)
11328 {
11329 if (hasAnchor(name))
11330 qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
11331 QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name);
11332 mPositions.append(newPosition);
11333 mAnchors.append(newPosition); // every position is also an anchor
11334 newPosition->setType(QCPItemPosition::ptPlotCoords);
11335 newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis);
11336 newPosition->setCoords(0, 0);
11337 return newPosition;
11338 }
11339
11340 /*! \internal
11341
11342 Creates a QCPItemAnchor, registers it with this item and returns a pointer to it. The specified
11343 \a name must be a unique string that is usually identical to the variable name of the anchor
11344 member (This is needed to provide the name based \ref anchor access to anchors).
11345
11346 The \a anchorId must be a number identifying the created anchor. It is recommended to create an
11347 enum (e.g. "AnchorIndex") for this on each item that uses anchors. This id is used by the anchor
11348 to identify itself when it calls QCPAbstractItem::anchorPixelPoint. That function then returns
11349 the correct pixel coordinates for the passed anchor id.
11350
11351 Don't delete anchors created by this function manually, as the item will take care of it.
11352
11353 Use this function in the constructor (initialization list) of the specific item subclass to
11354 create each anchor member. Don't create QCPItemAnchors with \b new yourself, because then they
11355 won't be registered with the item properly.
11356
11357 \see createPosition
11358 */
11359 QCPItemAnchor *QCPAbstractItem::createAnchor(const QString &name, int anchorId)
11360 {
11361 if (hasAnchor(name))
11362 qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
11363 QCPItemAnchor *newAnchor = new QCPItemAnchor(mParentPlot, this, name, anchorId);
11364 mAnchors.append(newAnchor);
11365 return newAnchor;
11366 }
11367
11368
11369 // ================================================================================
11370 // =================== QCPItemPosition
11371 // ================================================================================
11372
11373 /*! \class QCPItemPosition
11374 \brief Manages the position of an item.
11375
11376 Every item has at least one public QCPItemPosition member pointer which provides ways to position the
11377 item on the QCustomPlot surface. Some items have multiple positions, for example QCPItemRect has two:
11378 \a topLeft and \a bottomRight.
11379
11380 QCPItemPosition has a type (\ref PositionType) that can be set with \ref setType. This type defines
11381 how coordinates passed to \ref setCoords are to be interpreted, e.g. as absolute pixel coordinates, as
11382 plot coordinates of certain axes, etc.
11383
11384 Further, QCPItemPosition may have a parent QCPItemAnchor, see \ref setParentAnchor. (Note that every
11385 QCPItemPosition inherits from QCPItemAnchor and thus can itself be used as parent anchor for other
11386 positions.) This way you can tie multiple items together. If the QCPItemPosition has a parent, the
11387 coordinates set with \ref setCoords are considered to be absolute values in the reference frame of the
11388 parent anchor, where (0, 0) means directly ontop of the parent anchor. For example, You could attach
11389 the \a start position of a QCPItemLine to the \a bottom anchor of a QCPItemText to make the starting
11390 point of the line always be centered under the text label, no matter where the text is moved to, or is
11391 itself tied to.
11392
11393 To set the apparent pixel position on the QCustomPlot surface directly, use \ref setPixelPoint. This
11394 works no matter what type this QCPItemPosition is or what parent-child situation it is in, as \ref
11395 setPixelPoint transforms the coordinates appropriately, to make the position appear at the specified
11396 pixel values.
11397 */
11398
11399 /*!
11400 Creates a new QCPItemPosition. You shouldn't create QCPItemPosition instances directly, even if
11401 you want to make a new item subclass. Use \ref QCPAbstractItem::createPosition instead, as
11402 explained in the subclassing section of the QCPAbstractItem documentation.
11403 */
11404 QCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name) :
11405 QCPItemAnchor(parentPlot, parentItem, name),
11406 mPositionType(ptAbsolute),
11407 mKeyAxis(0),
11408 mValueAxis(0),
11409 mKey(0),
11410 mValue(0),
11411 mParentAnchor(0)
11412 {
11413 }
11414
11415 QCPItemPosition::~QCPItemPosition()
11416 {
11417 // unregister as parent at children:
11418 // Note: this is done in ~QCPItemAnchor again, but it's important QCPItemPosition does it itself, because only then
11419 // the setParentAnchor(0) call the correct QCPItemPosition::pixelPos function instead of QCPItemAnchor::pixelPos
11420 QList<QCPItemPosition*> currentChildren(mChildren.toList());
11421 for (int i=0; i<currentChildren.size(); ++i)
11422 currentChildren.at(i)->setParentAnchor(0); // this acts back on this anchor and child removes itself from mChildren
11423 // unregister as child in parent:
11424 if (mParentAnchor)
11425 mParentAnchor->removeChild(this);
11426 }
11427
11428 /*!
11429 Sets the type of the position. The type defines how the coordinates passed to \ref setCoords
11430 should be handled and how the QCPItemPosition should behave in the plot. Note that the position
11431 type \ref ptPlotCoords is only available (and sensible) when the position has no parent anchor
11432 (\ref setParentAnchor).
11433
11434 The possible values for \a type can be separated in two main categories:
11435
11436 \li The position is regarded as a point in plot coordinates. This corresponds to \ref ptPlotCoords
11437 and requires two axes that define the plot coordinate system. They can be specified with \ref setAxes.
11438 By default, the QCustomPlot's x- and yAxis are used.
11439
11440 \li The position is fixed on the QCustomPlot surface, i.e. independant of axis ranges. This
11441 corresponds to all other types, i.e. \ref ptAbsolute, \ref ptViewportRatio and \ref ptAxisRectRatio. They
11442 differ only in the way the absolute position is described, see the documentation of PositionType
11443 for details.
11444
11445 \note If the type is changed, the apparent pixel position on the plot is preserved. This means
11446 the coordinates as retrieved with coords() and set with \ref setCoords may change in the process.
11447 */
11448 void QCPItemPosition::setType(QCPItemPosition::PositionType type)
11449 {
11450 if (mPositionType != type)
11451 {
11452 QPointF pixelP = pixelPoint();
11453 mPositionType = type;
11454 setPixelPoint(pixelP);
11455 }
11456 }
11457
11458 /*!
11459 Sets the parent of this QCPItemPosition to \a parentAnchor. This means the position will now
11460 follow any position changes of the anchor. The local coordinate system of positions with a parent
11461 anchor always is absolute with (0, 0) being exactly on top of the parent anchor. (Hence the type
11462 shouldn't be \ref ptPlotCoords for positions with parent anchors.)
11463
11464 if \a keepPixelPosition is true, the current pixel position of the QCPItemPosition is preserved
11465 during reparenting. If it's set to false, the coordinates are set to (0, 0), i.e. the position
11466 will be exactly on top of the parent anchor.
11467
11468 To remove this QCPItemPosition from any parent anchor, set \a parentAnchor to 0.
11469
11470 \note If the QCPItemPosition previously had no parent and the type is \ref ptPlotCoords, the type
11471 is set to \ref ptAbsolute, to keep the position in a valid state.
11472 */
11473 bool QCPItemPosition::setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
11474 {
11475 // make sure self is not assigned as parent:
11476 if (parentAnchor == this)
11477 {
11478 qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
11479 return false;
11480 }
11481 // make sure no recursive parent-child-relationships are created:
11482 QCPItemAnchor *currentParent = parentAnchor;
11483 while (currentParent)
11484 {
11485 if (QCPItemPosition *currentParentPos = dynamic_cast<QCPItemPosition*>(currentParent))
11486 {
11487 // is a QCPItemPosition, might have further parent, so keep iterating
11488 if (currentParentPos == this)
11489 {
11490 qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
11491 return false;
11492 }
11493 currentParent = currentParentPos->mParentAnchor;
11494 } else
11495 {
11496 // is a QCPItemAnchor, can't have further parent, so just compare parent items
11497 if (currentParent->mParentItem == mParentItem)
11498 {
11499 qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
11500 return false;
11501 }
11502 break;
11503 }
11504 }
11505
11506 // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
11507 if (!mParentAnchor && mPositionType == ptPlotCoords)
11508 setType(ptAbsolute);
11509
11510 // save pixel position:
11511 QPointF pixelP;
11512 if (keepPixelPosition)
11513 pixelP = pixelPoint();
11514 // unregister at current parent anchor:
11515 if (mParentAnchor)
11516 mParentAnchor->removeChild(this);
11517 // register at new parent anchor:
11518 if (parentAnchor)
11519 parentAnchor->addChild(this);
11520 mParentAnchor = parentAnchor;
11521 // restore pixel position under new parent:
11522 if (keepPixelPosition)
11523 setPixelPoint(pixelP);
11524 else
11525 setCoords(0, 0);
11526 return true;
11527 }
11528
11529 /*!
11530 Sets the coordinates of this QCPItemPosition. What the coordinates mean, is defined by the type
11531 (\ref setType).
11532
11533 For example, if the type is \ref ptAbsolute, \a key and \a value mean the x and y pixel position
11534 on the QCustomPlot surface where the origin (0, 0) is in the top left corner of the QCustomPlot
11535 viewport. If the type is \ref ptPlotCoords, \a key and \a value mean a point in the plot
11536 coordinate system defined by the axes set by \ref setAxes. (By default the QCustomPlot's x- and
11537 yAxis.)
11538
11539 \see setPixelPoint
11540 */
11541 void QCPItemPosition::setCoords(double key, double value)
11542 {
11543 mKey = key;
11544 mValue = value;
11545 }
11546
11547 /*! \overload
11548
11549 Sets the coordinates as a QPointF \a pos where pos.x has the meaning of \a key and pos.y the
11550 meaning of \a value of the \ref setCoords(double key, double value) function.
11551 */
11552 void QCPItemPosition::setCoords(const QPointF &pos)
11553 {
11554 setCoords(pos.x(), pos.y());
11555 }
11556
11557 /*!
11558 Returns the final absolute pixel position of the QCPItemPosition on the QCustomPlot surface. It
11559 includes all effects of type (\ref setType) and possible parent anchors (\ref setParentAnchor).
11560
11561 \see setPixelPoint
11562 */
11563 QPointF QCPItemPosition::pixelPoint() const
11564 {
11565 switch (mPositionType)
11566 {
11567 case ptAbsolute:
11568 {
11569 if (mParentAnchor)
11570 return QPointF(mKey, mValue) + mParentAnchor->pixelPoint();
11571 else
11572 return QPointF(mKey, mValue);
11573 }
11574
11575 case ptViewportRatio:
11576 {
11577 if (mParentAnchor)
11578 {
11579 return QPointF(mKey*mParentPlot->viewport().width(),
11580 mValue*mParentPlot->viewport().height()) + mParentAnchor->pixelPoint();
11581 } else
11582 {
11583 return QPointF(mKey*mParentPlot->viewport().width(),
11584 mValue*mParentPlot->viewport().height()) + mParentPlot->viewport().topLeft();
11585 }
11586 }
11587
11588 case ptAxisRectRatio:
11589 {
11590 if (mParentAnchor)
11591 {
11592 return QPointF(mKey*mParentPlot->axisRect().width(),
11593 mValue*mParentPlot->axisRect().height()) + mParentAnchor->pixelPoint();
11594 } else
11595 {
11596 return QPointF(mKey*mParentPlot->axisRect().width(),
11597 mValue*mParentPlot->axisRect().height()) + mParentPlot->axisRect().topLeft();
11598 }
11599 }
11600
11601 case ptPlotCoords:
11602 {
11603 double x, y;
11604 if (mKeyAxis && mValueAxis)
11605 {
11606 // both key and value axis are given, translate key/value to x/y coordinates:
11607 if (mKeyAxis->orientation() == Qt::Horizontal)
11608 {
11609 x = mKeyAxis->coordToPixel(mKey);
11610 y = mValueAxis->coordToPixel(mValue);
11611 } else
11612 {
11613 y = mKeyAxis->coordToPixel(mKey);
11614 x = mValueAxis->coordToPixel(mValue);
11615 }
11616 } else if (mKeyAxis)
11617 {
11618 // only key axis is given, depending on orientation only transform x or y to key coordinate, other stays pixel:
11619 if (mKeyAxis->orientation() == Qt::Horizontal)
11620 {
11621 x = mKeyAxis->coordToPixel(mKey);
11622 y = mValue;
11623 } else
11624 {
11625 y = mKeyAxis->coordToPixel(mKey);
11626 x = mValue;
11627 }
11628 } else if (mValueAxis)
11629 {
11630 // only value axis is given, depending on orientation only transform x or y to value coordinate, other stays pixel:
11631 if (mValueAxis->orientation() == Qt::Horizontal)
11632 {
11633 x = mValueAxis->coordToPixel(mValue);
11634 y = mKey;
11635 } else
11636 {
11637 y = mValueAxis->coordToPixel(mValue);
11638 x = mKey;
11639 }
11640 } else
11641 {
11642 // no axis given, basically the same as if mAnchorType were atNone
11643 x = mKey;
11644 y = mValue;
11645 }
11646 return QPointF(x, y);
11647 }
11648 }
11649 return QPointF();
11650 }
11651
11652 /*!
11653 When \ref setType is ptPlotCoords, this function may be used to specify the axes the coordinates set
11654 with \ref setCoords relate to.
11655 */
11656 void QCPItemPosition::setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
11657 {
11658 mKeyAxis = keyAxis;
11659 mValueAxis = valueAxis;
11660 }
11661
11662 /*!
11663 Sets the apparent pixel position. This works no matter what type this QCPItemPosition is or what
11664 parent-child situation it is in, as \ref setPixelPoint transforms the coordinates appropriately, to
11665 make the position appear at the specified pixel values.
11666
11667 Only if the type is \ref ptAbsolute and no parent anchor is set, this function is identical to \ref
11668 setCoords.
11669
11670 \see setCoords
11671 */
11672 void QCPItemPosition::setPixelPoint(const QPointF &pixelPoint)
11673 {
11674 switch (mPositionType)
11675 {
11676 case ptAbsolute:
11677 {
11678 if (mParentAnchor)
11679 setCoords(pixelPoint-mParentAnchor->pixelPoint());
11680 else
11681 setCoords(pixelPoint);
11682 break;
11683 }
11684
11685 case ptViewportRatio:
11686 {
11687 if (mParentAnchor)
11688 {
11689 QPointF p(pixelPoint-mParentAnchor->pixelPoint());
11690 p.rx() /= (double)mParentPlot->viewport().width();
11691 p.ry() /= (double)mParentPlot->viewport().height();
11692 setCoords(p);
11693 } else
11694 {
11695 QPointF p(pixelPoint-mParentPlot->viewport().topLeft());
11696 p.rx() /= (double)mParentPlot->viewport().width();
11697 p.ry() /= (double)mParentPlot->viewport().height();
11698 setCoords(p);
11699 }
11700 break;
11701 }
11702
11703 case ptAxisRectRatio:
11704 {
11705 if (mParentAnchor)
11706 {
11707 QPointF p(pixelPoint-mParentAnchor->pixelPoint());
11708 p.rx() /= (double)mParentPlot->axisRect().width();
11709 p.ry() /= (double)mParentPlot->axisRect().height();
11710 setCoords(p);
11711 } else
11712 {
11713 QPointF p(pixelPoint-mParentPlot->axisRect().topLeft());
11714 p.rx() /= (double)mParentPlot->axisRect().width();
11715 p.ry() /= (double)mParentPlot->axisRect().height();
11716 setCoords(p);
11717 }
11718 break;
11719 }
11720
11721 case ptPlotCoords:
11722 {
11723 double newKey, newValue;
11724 if (mKeyAxis && mValueAxis)
11725 {
11726 // both key and value axis are given, translate point to key/value coordinates:
11727 if (mKeyAxis->orientation() == Qt::Horizontal)
11728 {
11729 newKey = mKeyAxis->pixelToCoord(pixelPoint.x());
11730 newValue = mValueAxis->pixelToCoord(pixelPoint.y());
11731 } else
11732 {
11733 newKey = mKeyAxis->pixelToCoord(pixelPoint.y());
11734 newValue = mValueAxis->pixelToCoord(pixelPoint.x());
11735 }
11736 } else if (mKeyAxis)
11737 {
11738 // only key axis is given, depending on orientation only transform x or y to key coordinate, other stays pixel:
11739 if (mKeyAxis->orientation() == Qt::Horizontal)
11740 {
11741 newKey = mKeyAxis->pixelToCoord(pixelPoint.x());
11742 newValue = pixelPoint.y();
11743 } else
11744 {
11745 newKey = mKeyAxis->pixelToCoord(pixelPoint.y());
11746 newValue = pixelPoint.x();
11747 }
11748 } else if (mValueAxis)
11749 {
11750 // only value axis is given, depending on orientation only transform x or y to value coordinate, other stays pixel:
11751 if (mValueAxis->orientation() == Qt::Horizontal)
11752 {
11753 newKey = pixelPoint.y();
11754 newValue = mValueAxis->pixelToCoord(pixelPoint.x());
11755 } else
11756 {
11757 newKey = pixelPoint.x();
11758 newValue = mValueAxis->pixelToCoord(pixelPoint.y());
11759 }
11760 } else
11761 {
11762 // no axis given, basically the same as if mAnchorType were atNone
11763 newKey = pixelPoint.x();
11764 newValue = pixelPoint.y();
11765 }
11766 setCoords(newKey, newValue);
11767 break;
11768 }
11769 }
11770 }
11771
11772
11773 // ================================================================================
11774 // =================== QCPItemStraightLine
11775 // ================================================================================
11776
11777 /*! \class QCPItemStraightLine
11778 \brief A straight line that spans infinitely in both directions
11779
11780 \image html QCPItemStraightLine.png "Straight line example. Blue dotted circles are anchors, solid blue discs are positions."
11781
11782 It has two positions, \a point1 and \a point2, which define the straight line.
11783 */
11784
11785 /*!
11786 Creates a straight line item and sets default values.
11787
11788 The constructed item can be added to the plot with QCustomPlot::addItem.
11789 */
11790 QCPItemStraightLine::QCPItemStraightLine(QCustomPlot *parentPlot) :
11791 QCPAbstractItem(parentPlot),
11792 point1(createPosition("point1")),
11793 point2(createPosition("point2"))
11794 {
11795 point1->setCoords(0, 0);
11796 point2->setCoords(1, 1);
11797
11798 setPen(QPen(Qt::black));
11799 setSelectedPen(QPen(Qt::blue,2));
11800 }
11801
11802 QCPItemStraightLine::~QCPItemStraightLine()
11803 {
11804 }
11805
11806 /*!
11807 Sets the pen that will be used to draw the line
11808
11809 \see setSelectedPen
11810 */
11811 void QCPItemStraightLine::setPen(const QPen &pen)
11812 {
11813 mPen = pen;
11814 }
11815
11816 /*!
11817 Sets the pen that will be used to draw the line when selected
11818
11819 \see setPen, setSelected
11820 */
11821 void QCPItemStraightLine::setSelectedPen(const QPen &pen)
11822 {
11823 mSelectedPen = pen;
11824 }
11825
11826 /* inherits documentation from base class */
11827 double QCPItemStraightLine::selectTest(const QPointF &pos) const
11828 {
11829 if (!mVisible)
11830 return -1;
11831
11832 return distToStraightLine(QVector2D(point1->pixelPoint()), QVector2D(point2->pixelPoint()-point1->pixelPoint()), QVector2D(pos));
11833 }
11834
11835 /* inherits documentation from base class */
11836 void QCPItemStraightLine::draw(QCPPainter *painter)
11837 {
11838 QVector2D start(point1->pixelPoint());
11839 QVector2D end(point2->pixelPoint());
11840 // get visible segment of straight line inside clipRect:
11841 double clipPad = mainPen().widthF();
11842 QLineF line = getRectClippedStraightLine(start, end-start, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
11843 // paint visible segment, if existent:
11844 if (!line.isNull())
11845 {
11846 painter->setPen(mainPen());
11847 painter->drawLine(line);
11848 }
11849 }
11850
11851 /*! \internal
11852
11853 finds the shortest distance of \a point to the straight line defined by the base point \a
11854 base and the direction vector \a vec.
11855
11856 This is a helper function for \ref selectTest.
11857 */
11858 double QCPItemStraightLine::distToStraightLine(const QVector2D &base, const QVector2D &vec, const QVector2D &point) const
11859 {
11860 return qAbs((base.y()-point.y())*vec.x()-(base.x()-point.x())*vec.y())/vec.length();
11861 }
11862
11863 /*! \internal
11864
11865 Returns the section of the straight line defined by \a base and direction vector \a
11866 vec, that is visible in the specified \a rect.
11867
11868 This is a helper function for \ref draw.
11869 */
11870 QLineF QCPItemStraightLine::getRectClippedStraightLine(const QVector2D &base, const QVector2D &vec, const QRect &rect) const
11871 {
11872 double bx, by;
11873 double gamma;
11874 QLineF result;
11875 if (vec.x() == 0 && vec.y() == 0)
11876 return result;
11877 if (qFuzzyIsNull(vec.x())) // line is vertical
11878 {
11879 // check top of rect:
11880 bx = rect.left();
11881 by = rect.top();
11882 gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
11883 if (gamma >= 0 && gamma <= rect.width())
11884 result.setLine(bx+gamma, rect.top(), bx+gamma, rect.bottom()); // no need to check bottom because we know line is vertical
11885 } else if (qFuzzyIsNull(vec.y())) // line is horizontal
11886 {
11887 // check left of rect:
11888 bx = rect.left();
11889 by = rect.top();
11890 gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
11891 if (gamma >= 0 && gamma <= rect.height())
11892 result.setLine(rect.left(), by+gamma, rect.right(), by+gamma); // no need to check right because we know line is horizontal
11893 } else // line is skewed
11894 {
11895 QList<QVector2D> pointVectors;
11896 // check top of rect:
11897 bx = rect.left();
11898 by = rect.top();
11899 gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
11900 if (gamma >= 0 && gamma <= rect.width())
11901 pointVectors.append(QVector2D(bx+gamma, by));
11902 // check bottom of rect:
11903 bx = rect.left();
11904 by = rect.bottom();
11905 gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
11906 if (gamma >= 0 && gamma <= rect.width())
11907 pointVectors.append(QVector2D(bx+gamma, by));
11908 // check left of rect:
11909 bx = rect.left();
11910 by = rect.top();
11911 gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
11912 if (gamma >= 0 && gamma <= rect.height())
11913 pointVectors.append(QVector2D(bx, by+gamma));
11914 // check right of rect:
11915 bx = rect.right();
11916 by = rect.top();
11917 gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
11918 if (gamma >= 0 && gamma <= rect.height())
11919 pointVectors.append(QVector2D(bx, by+gamma));
11920
11921 // evaluate points:
11922 if (pointVectors.size() == 2)
11923 {
11924 result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
11925 } else if (pointVectors.size() > 2)
11926 {
11927 // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
11928 double distSqrMax = 0;
11929 QVector2D pv1, pv2;
11930 for (int i=0; i<pointVectors.size()-1; ++i)
11931 {
11932 for (int k=i+1; k<pointVectors.size(); ++k)
11933 {
11934 double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
11935 if (distSqr > distSqrMax)
11936 {
11937 pv1 = pointVectors.at(i);
11938 pv2 = pointVectors.at(k);
11939 distSqrMax = distSqr;
11940 }
11941 }
11942 }
11943 result.setPoints(pv1.toPointF(), pv2.toPointF());
11944 }
11945 }
11946 return result;
11947 }
11948
11949 /*! \internal
11950
11951 Returns the pen that should be used for drawing lines. Returns mPen when the
11952 item is not selected and mSelectedPen when it is.
11953 */
11954 QPen QCPItemStraightLine::mainPen() const
11955 {
11956 return mSelected ? mSelectedPen : mPen;
11957 }
11958
11959
11960 // ================================================================================
11961 // =================== QCPItemLine
11962 // ================================================================================
11963
11964 /*! \class QCPItemLine
11965 \brief A line from one point to another
11966
11967 \image html QCPItemLine.png "Line example. Blue dotted circles are anchors, solid blue discs are positions."
11968
11969 It has two positions, \a start and \a end, which define the end points of the line.
11970
11971 With \ref setHead and \ref setTail you may set different line ending styles, e.g. to create an arrow.
11972 */
11973
11974 /*!
11975 Creates a line item and sets default values.
11976
11977 The constructed item can be added to the plot with QCustomPlot::addItem.
11978 */
11979 QCPItemLine::QCPItemLine(QCustomPlot *parentPlot) :
11980 QCPAbstractItem(parentPlot),
11981 start(createPosition("start")),
11982 end(createPosition("end"))
11983 {
11984 start->setCoords(0, 0);
11985 end->setCoords(1, 1);
11986
11987 setPen(QPen(Qt::black));
11988 setSelectedPen(QPen(Qt::blue,2));
11989 }
11990
11991 QCPItemLine::~QCPItemLine()
11992 {
11993 }
11994
11995 /*!
11996 Sets the pen that will be used to draw the line
11997
11998 \see setSelectedPen
11999 */
12000 void QCPItemLine::setPen(const QPen &pen)
12001 {
12002 mPen = pen;
12003 }
12004
12005 /*!
12006 Sets the pen that will be used to draw the line when selected
12007
12008 \see setPen, setSelected
12009 */
12010 void QCPItemLine::setSelectedPen(const QPen &pen)
12011 {
12012 mSelectedPen = pen;
12013 }
12014
12015 /*!
12016 Sets the line ending style of the head. The head corresponds to the \a end position.
12017
12018 Note that due to the overloaded QCPLineEnding constructor, you may directly specify
12019 a QCPLineEnding::EndingStyle here, e.g. \code setHead(QCPLineEnding::esSpikeArrow) \endcode
12020
12021 \see setTail
12022 */
12023 void QCPItemLine::setHead(const QCPLineEnding &head)
12024 {
12025 mHead = head;
12026 }
12027
12028 /*!
12029 Sets the line ending style of the tail. The tail corresponds to the \a start position.
12030
12031 Note that due to the overloaded QCPLineEnding constructor, you may directly specify
12032 a QCPLineEnding::EndingStyle here, e.g. \code setTail(QCPLineEnding::esSpikeArrow) \endcode
12033
12034 \see setHead
12035 */
12036 void QCPItemLine::setTail(const QCPLineEnding &tail)
12037 {
12038 mTail = tail;
12039 }
12040
12041 /* inherits documentation from base class */
12042 double QCPItemLine::selectTest(const QPointF &pos) const
12043 {
12044 if (!mVisible)
12045 return -1;
12046
12047 return qSqrt(distSqrToLine(start->pixelPoint(), end->pixelPoint(), pos));
12048 }
12049
12050 /* inherits documentation from base class */
12051 void QCPItemLine::draw(QCPPainter *painter)
12052 {
12053 QVector2D startVec(start->pixelPoint());
12054 QVector2D endVec(end->pixelPoint());
12055 if (startVec.toPoint() == endVec.toPoint())
12056 return;
12057 // get visible segment of straight line inside clipRect:
12058 double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance());
12059 clipPad = qMax(clipPad, mainPen().widthF());
12060 QLineF line = getRectClippedLine(startVec, endVec, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
12061 // paint visible segment, if existent:
12062 if (!line.isNull())
12063 {
12064 painter->setPen(mainPen());
12065 painter->drawLine(line);
12066 painter->setBrush(Qt::SolidPattern);
12067 if (mTail.style() != QCPLineEnding::esNone)
12068 mTail.draw(painter, startVec, startVec-endVec);
12069 if (mHead.style() != QCPLineEnding::esNone)
12070 mHead.draw(painter, endVec, endVec-startVec);
12071 }
12072 }
12073
12074 /*! \internal
12075
12076 Returns the section of the line defined by \a start and \a end, that is visible in the specified
12077 \a rect.
12078
12079 This is a helper function for \ref draw.
12080 */
12081 QLineF QCPItemLine::getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const
12082 {
12083 bool containsStart = rect.contains(start.x(), start.y());
12084 bool containsEnd = rect.contains(end.x(), end.y());
12085 if (containsStart && containsEnd)
12086 return QLineF(start.toPointF(), end.toPointF());
12087
12088 QVector2D base = start;
12089 QVector2D vec = end-start;
12090 double bx, by;
12091 double gamma, mu;
12092 QLineF result;
12093 QList<QVector2D> pointVectors;
12094
12095 if (!qFuzzyIsNull(vec.y())) // line is not horizontal
12096 {
12097 // check top of rect:
12098 bx = rect.left();
12099 by = rect.top();
12100 mu = (by-base.y())/vec.y();
12101 if (mu >= 0 && mu <= 1)
12102 {
12103 gamma = base.x()-bx + mu*vec.x();
12104 if (gamma >= 0 && gamma <= rect.width())
12105 pointVectors.append(QVector2D(bx+gamma, by));
12106 }
12107 // check bottom of rect:
12108 bx = rect.left();
12109 by = rect.bottom();
12110 mu = (by-base.y())/vec.y();
12111 if (mu >= 0 && mu <= 1)
12112 {
12113 gamma = base.x()-bx + mu*vec.x();
12114 if (gamma >= 0 && gamma <= rect.width())
12115 pointVectors.append(QVector2D(bx+gamma, by));
12116 }
12117 }
12118 if (!qFuzzyIsNull(vec.x())) // line is not vertical
12119 {
12120 // check left of rect:
12121 bx = rect.left();
12122 by = rect.top();
12123 mu = (bx-base.x())/vec.x();
12124 if (mu >= 0 && mu <= 1)
12125 {
12126 gamma = base.y()-by + mu*vec.y();
12127 if (gamma >= 0 && gamma <= rect.height())
12128 pointVectors.append(QVector2D(bx, by+gamma));
12129 }
12130 // check right of rect:
12131 bx = rect.right();
12132 by = rect.top();
12133 mu = (bx-base.x())/vec.x();
12134 if (mu >= 0 && mu <= 1)
12135 {
12136 gamma = base.y()-by + mu*vec.y();
12137 if (gamma >= 0 && gamma <= rect.height())
12138 pointVectors.append(QVector2D(bx, by+gamma));
12139 }
12140 }
12141
12142 if (containsStart)
12143 pointVectors.append(start);
12144 if (containsEnd)
12145 pointVectors.append(end);
12146
12147 // evaluate points:
12148 if (pointVectors.size() == 2)
12149 {
12150 result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
12151 } else if (pointVectors.size() > 2)
12152 {
12153 // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
12154 double distSqrMax = 0;
12155 QVector2D pv1, pv2;
12156 for (int i=0; i<pointVectors.size()-1; ++i)
12157 {
12158 for (int k=i+1; k<pointVectors.size(); ++k)
12159 {
12160 double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
12161 if (distSqr > distSqrMax)
12162 {
12163 pv1 = pointVectors.at(i);
12164 pv2 = pointVectors.at(k);
12165 distSqrMax = distSqr;
12166 }
12167 }
12168 }
12169 result.setPoints(pv1.toPointF(), pv2.toPointF());
12170 }
12171 return result;
12172 }
12173
12174 /*! \internal
12175
12176 Returns the pen that should be used for drawing lines. Returns mPen when the
12177 item is not selected and mSelectedPen when it is.
12178 */
12179 QPen QCPItemLine::mainPen() const
12180 {
12181 return mSelected ? mSelectedPen : mPen;
12182 }
12183
12184
12185 // ================================================================================
12186 // =================== QCPItemEllipse
12187 // ================================================================================
12188
12189 /*! \class QCPItemEllipse
12190 \brief An ellipse
12191
12192 \image html QCPItemEllipse.png "Ellipse example. Blue dotted circles are anchors, solid blue discs are positions."
12193
12194 It has two positions, \a topLeft and \a bottomRight, which define the rect the ellipse will be drawn in.
12195 */
12196
12197 /*!
12198 Creates an ellipse item and sets default values.
12199
12200 The constructed item can be added to the plot with QCustomPlot::addItem.
12201 */
12202 QCPItemEllipse::QCPItemEllipse(QCustomPlot *parentPlot) :
12203 QCPAbstractItem(parentPlot),
12204 topLeft(createPosition("topLeft")),
12205 bottomRight(createPosition("bottomRight")),
12206 topLeftRim(createAnchor("topLeftRim", aiTopLeftRim)),
12207 top(createAnchor("top", aiTop)),
12208 topRightRim(createAnchor("topRightRim", aiTopRightRim)),
12209 right(createAnchor("right", aiRight)),
12210 bottomRightRim(createAnchor("bottomRightRim", aiBottomRightRim)),
12211 bottom(createAnchor("bottom", aiBottom)),
12212 bottomLeftRim(createAnchor("bottomLeftRim", aiBottomLeftRim)),
12213 left(createAnchor("left", aiLeft))
12214 {
12215 topLeft->setCoords(0, 1);
12216 bottomRight->setCoords(1, 0);
12217
12218 setPen(QPen(Qt::black));
12219 setSelectedPen(QPen(Qt::blue, 2));
12220 setBrush(Qt::NoBrush);
12221 setSelectedBrush(Qt::NoBrush);
12222 }
12223
12224 QCPItemEllipse::~QCPItemEllipse()
12225 {
12226 }
12227
12228 /*!
12229 Sets the pen that will be used to draw the line of the ellipse
12230
12231 \see setSelectedPen, setBrush
12232 */
12233 void QCPItemEllipse::setPen(const QPen &pen)
12234 {
12235 mPen = pen;
12236 }
12237
12238 /*!
12239 Sets the pen that will be used to draw the line of the ellipse when selected
12240
12241 \see setPen, setSelected
12242 */
12243 void QCPItemEllipse::setSelectedPen(const QPen &pen)
12244 {
12245 mSelectedPen = pen;
12246 }
12247
12248 /*!
12249 Sets the brush that will be used to fill the ellipse. To disable filling, set \a brush to
12250 Qt::NoBrush.
12251
12252 \see setSelectedBrush, setPen
12253 */
12254 void QCPItemEllipse::setBrush(const QBrush &brush)
12255 {
12256 mBrush = brush;
12257 }
12258
12259 /*!
12260 Sets the brush that will be used to fill the ellipse when selected. To disable filling, set \a
12261 brush to Qt::NoBrush.
12262
12263 \see setBrush
12264 */
12265 void QCPItemEllipse::setSelectedBrush(const QBrush &brush)
12266 {
12267 mSelectedBrush = brush;
12268 }
12269
12270 /* inherits documentation from base class */
12271 double QCPItemEllipse::selectTest(const QPointF &pos) const
12272 {
12273 double result = -1;
12274 QPointF p1 = topLeft->pixelPoint();
12275 QPointF p2 = bottomRight->pixelPoint();
12276 QPointF center((p1+p2)/2.0);
12277 double a = qAbs(p1.x()-p2.x())/2.0;
12278 double b = qAbs(p1.y()-p2.y())/2.0;
12279 double x = pos.x()-center.x();
12280 double y = pos.y()-center.y();
12281
12282 // distance to border:
12283 double c = 1.0/qSqrt(x*x/(a*a)+y*y/(b*b));
12284 result = qAbs(c-1)*qSqrt(x*x+y*y);
12285 // filled ellipse, allow click inside to count as hit:
12286 if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
12287 {
12288 if (x*x/(a*a) + y*y/(b*b) <= 1)
12289 result = mParentPlot->selectionTolerance()*0.99;
12290 }
12291 return result;
12292 }
12293
12294 /* inherits documentation from base class */
12295 void QCPItemEllipse::draw(QCPPainter *painter)
12296 {
12297 QPointF p1 = topLeft->pixelPoint();
12298 QPointF p2 = bottomRight->pixelPoint();
12299 if (p1.toPoint() == p2.toPoint())
12300 return;
12301 QRectF ellipseRect = QRectF(p1, p2).normalized();
12302 QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
12303 if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse is visible in cliprect
12304 {
12305 painter->setPen(mainPen());
12306 painter->setBrush(mainBrush());
12307 try
12308 {
12309 painter->drawEllipse(ellipseRect);
12310 } catch (...)
12311 {
12312 qDebug() << Q_FUNC_INFO << "Item too large for memory, setting invisible";
12313 setVisible(false);
12314 }
12315 }
12316 }
12317
12318 /* inherits documentation from base class */
12319 QPointF QCPItemEllipse::anchorPixelPoint(int anchorId) const
12320 {
12321 QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
12322 switch (anchorId)
12323 {
12324 case aiTopLeftRim: return rect.center()+(rect.topLeft()-rect.center())*1/qSqrt(2);
12325 case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
12326 case aiTopRightRim: return rect.center()+(rect.topRight()-rect.center())*1/qSqrt(2);
12327 case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
12328 case aiBottomRightRim: return rect.center()+(rect.bottomRight()-rect.center())*1/qSqrt(2);
12329 case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
12330 case aiBottomLeftRim: return rect.center()+(rect.bottomLeft()-rect.center())*1/qSqrt(2);
12331 case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;;
12332 }
12333
12334 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
12335 return QPointF();
12336 }
12337
12338 /*! \internal
12339
12340 Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
12341 and mSelectedPen when it is.
12342 */
12343 QPen QCPItemEllipse::mainPen() const
12344 {
12345 return mSelected ? mSelectedPen : mPen;
12346 }
12347
12348 /*! \internal
12349
12350 Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
12351 is not selected and mSelectedBrush when it is.
12352 */
12353 QBrush QCPItemEllipse::mainBrush() const
12354 {
12355 return mSelected ? mSelectedBrush : mBrush;
12356 }
12357
12358
12359 // ================================================================================
12360 // =================== QCPItemRect
12361 // ================================================================================
12362
12363 /*! \class QCPItemRect
12364 \brief A rectangle
12365
12366 \image html QCPItemRect.png "Rectangle example. Blue dotted circles are anchors, solid blue discs are positions."
12367
12368 It has two positions, \a topLeft and \a bottomRight, which define the rectangle.
12369 */
12370
12371 /*!
12372 Creates a rectangle item and sets default values.
12373
12374 The constructed item can be added to the plot with QCustomPlot::addItem.
12375 */
12376 QCPItemRect::QCPItemRect(QCustomPlot *parentPlot) :
12377 QCPAbstractItem(parentPlot),
12378 topLeft(createPosition("topLeft")),
12379 bottomRight(createPosition("bottomRight")),
12380 top(createAnchor("top", aiTop)),
12381 topRight(createAnchor("topRight", aiTopRight)),
12382 right(createAnchor("right", aiRight)),
12383 bottom(createAnchor("bottom", aiBottom)),
12384 bottomLeft(createAnchor("bottomLeft", aiBottomLeft)),
12385 left(createAnchor("left", aiLeft))
12386 {
12387 topLeft->setCoords(0, 1);
12388 bottomRight->setCoords(1, 0);
12389
12390 setPen(QPen(Qt::black));
12391 setSelectedPen(QPen(Qt::blue,2));
12392 setBrush(Qt::NoBrush);
12393 setSelectedBrush(Qt::NoBrush);
12394 }
12395
12396 QCPItemRect::~QCPItemRect()
12397 {
12398 }
12399
12400 /*!
12401 Sets the pen that will be used to draw the line of the rectangle
12402
12403 \see setSelectedPen, setBrush
12404 */
12405 void QCPItemRect::setPen(const QPen &pen)
12406 {
12407 mPen = pen;
12408 }
12409
12410 /*!
12411 Sets the pen that will be used to draw the line of the rectangle when selected
12412
12413 \see setPen, setSelected
12414 */
12415 void QCPItemRect::setSelectedPen(const QPen &pen)
12416 {
12417 mSelectedPen = pen;
12418 }
12419
12420 /*!
12421 Sets the brush that will be used to fill the rectangle. To disable filling, set \a brush to
12422 Qt::NoBrush.
12423
12424 \see setSelectedBrush, setPen
12425 */
12426 void QCPItemRect::setBrush(const QBrush &brush)
12427 {
12428 mBrush = brush;
12429 }
12430
12431 /*!
12432 Sets the brush that will be used to fill the rectangle when selected. To disable filling, set \a
12433 brush to Qt::NoBrush.
12434
12435 \see setBrush
12436 */
12437 void QCPItemRect::setSelectedBrush(const QBrush &brush)
12438 {
12439 mSelectedBrush = brush;
12440 }
12441
12442 /* inherits documentation from base class */
12443 double QCPItemRect::selectTest(const QPointF &pos) const
12444 {
12445 if (!mVisible)
12446 return -1;
12447
12448 QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint()).normalized();
12449 bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
12450 return rectSelectTest(rect, pos, filledRect);
12451 }
12452
12453 /* inherits documentation from base class */
12454 void QCPItemRect::draw(QCPPainter *painter)
12455 {
12456 QPointF p1 = topLeft->pixelPoint();
12457 QPointF p2 = bottomRight->pixelPoint();
12458 if (p1.toPoint() == p2.toPoint())
12459 return;
12460 QRectF rect = QRectF(p1, p2).normalized();
12461 double clipPad = mainPen().widthF();
12462 QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
12463 if (boundingRect.intersects(clipRect())) // only draw if bounding rect of rect item is visible in cliprect
12464 {
12465 painter->setPen(mainPen());
12466 painter->setBrush(mainBrush());
12467 painter->drawRect(rect);
12468 }
12469 }
12470
12471 /* inherits documentation from base class */
12472 QPointF QCPItemRect::anchorPixelPoint(int anchorId) const
12473 {
12474 QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
12475 switch (anchorId)
12476 {
12477 case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
12478 case aiTopRight: return rect.topRight();
12479 case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
12480 case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
12481 case aiBottomLeft: return rect.bottomLeft();
12482 case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;;
12483 }
12484
12485 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
12486 return QPointF();
12487 }
12488
12489 /*! \internal
12490
12491 Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
12492 and mSelectedPen when it is.
12493 */
12494 QPen QCPItemRect::mainPen() const
12495 {
12496 return mSelected ? mSelectedPen : mPen;
12497 }
12498
12499 /*! \internal
12500
12501 Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
12502 is not selected and mSelectedBrush when it is.
12503 */
12504 QBrush QCPItemRect::mainBrush() const
12505 {
12506 return mSelected ? mSelectedBrush : mBrush;
12507 }
12508
12509
12510 // ================================================================================
12511 // =================== QCPItemPixmap
12512 // ================================================================================
12513
12514 /*! \class QCPItemPixmap
12515 \brief An arbitrary pixmap
12516
12517 \image html QCPItemPixmap.png "Pixmap example. Blue dotted circles are anchors, solid blue discs are positions."
12518
12519 It has two positions, \a topLeft and \a bottomRight, which define the rectangle the pixmap will
12520 be drawn in. Depending on the scale setting (\ref setScaled), the pixmap will be either scaled to
12521 fit the rectangle or be drawn aligned to the topLeft position.
12522
12523 If scaling is enabled and \a topLeft is further to the bottom/right than \a bottomRight (as shown
12524 on the right side of the example image), the pixmap will be flipped in the respective
12525 orientations.
12526 */
12527
12528 /*!
12529 Creates a rectangle item and sets default values.
12530
12531 The constructed item can be added to the plot with QCustomPlot::addItem.
12532 */
12533 QCPItemPixmap::QCPItemPixmap(QCustomPlot *parentPlot) :
12534 QCPAbstractItem(parentPlot),
12535 topLeft(createPosition("topLeft")),
12536 bottomRight(createPosition("bottomRight")),
12537 top(createAnchor("top", aiTop)),
12538 topRight(createAnchor("topRight", aiTopRight)),
12539 right(createAnchor("right", aiRight)),
12540 bottom(createAnchor("bottom", aiBottom)),
12541 bottomLeft(createAnchor("bottomLeft", aiBottomLeft)),
12542 left(createAnchor("left", aiLeft))
12543 {
12544 topLeft->setCoords(0, 1);
12545 bottomRight->setCoords(1, 0);
12546
12547 setPen(Qt::NoPen);
12548 setSelectedPen(QPen(Qt::blue));
12549 setScaled(false, Qt::KeepAspectRatio);
12550 }
12551
12552 QCPItemPixmap::~QCPItemPixmap()
12553 {
12554 }
12555
12556 /*!
12557 Sets the pixmap that will be displayed.
12558 */
12559 void QCPItemPixmap::setPixmap(const QPixmap &pixmap)
12560 {
12561 mPixmap = pixmap;
12562 }
12563
12564 /*!
12565 Sets whether the pixmap will be scaled to fit the rectangle defined by the \a topLeft and \a
12566 bottomRight positions.
12567 */
12568 void QCPItemPixmap::setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode)
12569 {
12570 mScaled = scaled;
12571 mAspectRatioMode = aspectRatioMode;
12572 updateScaledPixmap();
12573 }
12574
12575 /*!
12576 Sets the pen that will be used to draw a border around the pixmap.
12577
12578 \see setSelectedPen, setBrush
12579 */
12580 void QCPItemPixmap::setPen(const QPen &pen)
12581 {
12582 mPen = pen;
12583 }
12584
12585 /*!
12586 Sets the pen that will be used to draw a border around the pixmap when selected
12587
12588 \see setPen, setSelected
12589 */
12590 void QCPItemPixmap::setSelectedPen(const QPen &pen)
12591 {
12592 mSelectedPen = pen;
12593 }
12594
12595 /* inherits documentation from base class */
12596 double QCPItemPixmap::selectTest(const QPointF &pos) const
12597 {
12598 if (!mVisible)
12599 return -1;
12600
12601 return rectSelectTest(getFinalRect(), pos, true);
12602 }
12603
12604 /* inherits documentation from base class */
12605 void QCPItemPixmap::draw(QCPPainter *painter)
12606 {
12607 bool flipHorz = false;
12608 bool flipVert = false;
12609 QRect rect = getFinalRect(&flipHorz, &flipVert);
12610 double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF();
12611 QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
12612 if (boundingRect.intersects(clipRect()))
12613 {
12614 updateScaledPixmap(rect, flipHorz, flipVert);
12615 painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap);
12616 QPen pen = mainPen();
12617 if (pen.style() != Qt::NoPen)
12618 {
12619 painter->setPen(pen);
12620 painter->setBrush(Qt::NoBrush);
12621 painter->drawRect(rect);
12622 }
12623 }
12624 }
12625
12626 /* inherits documentation from base class */
12627 QPointF QCPItemPixmap::anchorPixelPoint(int anchorId) const
12628 {
12629 bool flipHorz;
12630 bool flipVert;
12631 QRect rect = getFinalRect(&flipHorz, &flipVert);
12632 // we actually want denormal rects (negative width/height) here, so restore
12633 // the flipped state:
12634 if (flipHorz)
12635 rect.adjust(rect.width(), 0, -rect.width(), 0);
12636 if (flipVert)
12637 rect.adjust(0, rect.height(), 0, -rect.height());
12638
12639 switch (anchorId)
12640 {
12641 case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
12642 case aiTopRight: return rect.topRight();
12643 case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
12644 case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
12645 case aiBottomLeft: return rect.bottomLeft();
12646 case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;;
12647 }
12648
12649 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
12650 return QPointF();
12651 }
12652
12653 /*! \internal
12654
12655 Creates the buffered scaled image (\a mScaledPixmap) to fit the specified \a finalRect. The
12656 parameters \a flipHorz and \a flipVert control whether the resulting image shall be flipped
12657 horizontally or vertically. (This is used when \a topLeft is further to the bottom/right than \a
12658 bottomRight.)
12659
12660 This function only creates the scaled pixmap when the buffered pixmap has a different size than
12661 the expected result, so calling this function repeatedly, e.g. in the \ref draw function, does
12662 not cause expensive rescaling every time.
12663
12664 If scaling is disabled, sets mScaledPixmap to a null QPixmap.
12665 */
12666 void QCPItemPixmap::updateScaledPixmap(QRect finalRect, bool flipHorz, bool flipVert)
12667 {
12668 if (mScaled)
12669 {
12670 if (finalRect.isNull())
12671 finalRect = getFinalRect(&flipHorz, &flipVert);
12672 if (finalRect.size() != mScaledPixmap.size())
12673 {
12674 mScaledPixmap = mPixmap.scaled(finalRect.size(), mAspectRatioMode, Qt::SmoothTransformation);
12675 if (flipHorz || flipVert)
12676 mScaledPixmap = QPixmap::fromImage(mScaledPixmap.toImage().mirrored(flipHorz, flipVert));
12677 }
12678 } else if (!mScaledPixmap.isNull())
12679 mScaledPixmap = QPixmap();
12680 }
12681
12682 /*! \internal
12683
12684 Returns the final (tight) rect the pixmap is drawn in, depending on the current item positions
12685 and scaling settings.
12686
12687 The output parameters \a flippedHorz and \a flippedVert return whether the pixmap should be drawn
12688 flipped horizontally or vertically in the returned rect. (The returned rect itself is always
12689 normalized, i.e. the top left corner of the rect is actually further to the top/left than the
12690 bottom right corner). This is the case when the item position \a topLeft is further to the
12691 bottom/right than \a bottomRight.
12692
12693 If scaling is disabled, returns a rect with size of the original pixmap and the top left corner
12694 aligned with the item position \a topLeft. The position \a bottomRight is ignored.
12695 */
12696 QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const
12697 {
12698 QRect result;
12699 bool flipHorz = false;
12700 bool flipVert = false;
12701 QPoint p1 = topLeft->pixelPoint().toPoint();
12702 QPoint p2 = bottomRight->pixelPoint().toPoint();
12703 if (p1 == p2)
12704 return QRect(p1, QSize(0, 0));
12705 if (mScaled)
12706 {
12707 QSize newSize = QSize(p2.x()-p1.x(), p2.y()-p1.y());
12708 QPoint topLeft = p1;
12709 if (newSize.width() < 0)
12710 {
12711 flipHorz = true;
12712 newSize.rwidth() *= -1;
12713 topLeft.setX(p2.x());
12714 }
12715 if (newSize.height() < 0)
12716 {
12717 flipVert = true;
12718 newSize.rheight() *= -1;
12719 topLeft.setY(p2.y());
12720 }
12721 QSize scaledSize = mPixmap.size();
12722 scaledSize.scale(newSize, mAspectRatioMode);
12723 result = QRect(topLeft, scaledSize);
12724 } else
12725 {
12726 result = QRect(p1, mPixmap.size());
12727 }
12728 if (flippedHorz)
12729 *flippedHorz = flipHorz;
12730 if (flippedVert)
12731 *flippedVert = flipVert;
12732 return result;
12733 }
12734
12735 /*! \internal
12736
12737 Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
12738 and mSelectedPen when it is.
12739 */
12740 QPen QCPItemPixmap::mainPen() const
12741 {
12742 return mSelected ? mSelectedPen : mPen;
12743 }
12744
12745
12746 // ================================================================================
12747 // =================== QCPItemText
12748 // ================================================================================
12749
12750 /*! \class QCPItemText
12751 \brief A text label
12752
12753 \image html QCPItemText.png "Text example. Blue dotted circles are anchors, solid blue discs are positions."
12754
12755 Its position is defined by the member \a position and the setting of \ref setPositionAlignment.
12756 The latter controls which part of the text rect shall be aligned with \a position.
12757
12758 The text alignment itself (i.e. left, center, right) can be controlled with \ref
12759 setTextAlignment.
12760
12761 The text may be rotated around the \a position point with \ref setRotation.
12762 */
12763
12764 /*!
12765 Creates a text item and sets default values.
12766
12767 The constructed item can be added to the plot with QCustomPlot::addItem.
12768 */
12769 QCPItemText::QCPItemText(QCustomPlot *parentPlot) :
12770 QCPAbstractItem(parentPlot),
12771 position(createPosition("position")),
12772 topLeft(createAnchor("topLeft", aiTopLeft)),
12773 top(createAnchor("top", aiTop)),
12774 topRight(createAnchor("topRight", aiTopRight)),
12775 right(createAnchor("right", aiRight)),
12776 bottomRight(createAnchor("bottomRight", aiBottomRight)),
12777 bottom(createAnchor("bottom", aiBottom)),
12778 bottomLeft(createAnchor("bottomLeft", aiBottomLeft)),
12779 left(createAnchor("left", aiLeft))
12780 {
12781 position->setCoords(0, 0);
12782
12783 setRotation(0);
12784 setTextAlignment(Qt::AlignTop|Qt::AlignHCenter);
12785 setPositionAlignment(Qt::AlignCenter);
12786 setText("text");
12787
12788 setPen(Qt::NoPen);
12789 setSelectedPen(Qt::NoPen);
12790 setBrush(Qt::NoBrush);
12791 setSelectedBrush(Qt::NoBrush);
12792 setColor(Qt::black);
12793 setSelectedColor(Qt::blue);
12794 }
12795
12796 QCPItemText::~QCPItemText()
12797 {
12798 }
12799
12800 /*!
12801 Sets the color of the text.
12802 */
12803 void QCPItemText::setColor(const QColor &color)
12804 {
12805 mColor = color;
12806 }
12807
12808 /*!
12809 Sets the color of the text that will be used when the item is selected.
12810 */
12811 void QCPItemText::setSelectedColor(const QColor &color)
12812 {
12813 mSelectedColor = color;
12814 }
12815
12816 /*!
12817 Sets the pen that will be used do draw a rectangular border around the text. To disable the
12818 border, set \a pen to Qt::NoPen.
12819
12820 \see setSelectedPen, setBrush, setPadding
12821 */
12822 void QCPItemText::setPen(const QPen &pen)
12823 {
12824 mPen = pen;
12825 }
12826
12827 /*!
12828 Sets the pen that will be used do draw a rectangular border around the text, when the item is
12829 selected. To disable the border, set \a pen to Qt::NoPen.
12830
12831 \see setPen
12832 */
12833 void QCPItemText::setSelectedPen(const QPen &pen)
12834 {
12835 mSelectedPen = pen;
12836 }
12837
12838 /*!
12839 Sets the brush that will be used do fill the background of the text. To disable the
12840 background, set \a brush to Qt::NoBrush.
12841
12842 \see setSelectedBrush, setPen, setPadding
12843 */
12844 void QCPItemText::setBrush(const QBrush &brush)
12845 {
12846 mBrush = brush;
12847 }
12848
12849 /*!
12850 Sets the brush that will be used do fill the background of the text, when the item is selected. To disable the
12851 background, set \a brush to Qt::NoBrush.
12852
12853 \see setBrush
12854 */
12855 void QCPItemText::setSelectedBrush(const QBrush &brush)
12856 {
12857 mSelectedBrush = brush;
12858 }
12859
12860 /*!
12861 Sets the font of the text.
12862
12863 \see setSelectedFont, setColor
12864 */
12865 void QCPItemText::setFont(const QFont &font)
12866 {
12867 mFont = font;
12868 }
12869
12870 /*!
12871 Sets the font of the text that will be used when the item is selected.
12872
12873 \see setFont
12874 */
12875 void QCPItemText::setSelectedFont(const QFont &font)
12876 {
12877 mSelectedFont = font;
12878 }
12879
12880 /*!
12881 Sets the text that will be displayed. Multi-line texts are supported by inserting a line break
12882 character, e.g. '\n'.
12883
12884 \see setFont, setColor, setTextAlignment
12885 */
12886 void QCPItemText::setText(const QString &text)
12887 {
12888 mText = text;
12889 }
12890
12891 /*!
12892 Sets which point of the text rect shall be aligned with \a position.
12893
12894 Examples:
12895 \li If \a alignment is <tt>Qt::AlignHCenter | Qt::AlignTop</tt>, the text will be positioned such
12896 that the top of the text rect will be horizontally centered on \a position.
12897 \li If \a alignment is <tt>Qt::AlignLeft | Qt::AlignBottom</tt>, \a position will indicate the
12898 bottom left corner of the text rect.
12899
12900 If you want to control the alignment of (multi-lined) text within the text rect, use \ref
12901 setTextAlignment.
12902 */
12903 void QCPItemText::setPositionAlignment(Qt::Alignment alignment)
12904 {
12905 mPositionAlignment = alignment;
12906 }
12907
12908 /*!
12909 Controls how (multi-lined) text is aligned inside the text rect (typically Qt::AlignLeft, Qt::AlignCenter or Qt::AlignRight).
12910 */
12911 void QCPItemText::setTextAlignment(Qt::Alignment alignment)
12912 {
12913 mTextAlignment = alignment;
12914 }
12915
12916 /*!
12917 Sets the angle in degrees by which the text (and the text rectangle, if visible) will be rotated
12918 around \a position.
12919 */
12920 void QCPItemText::setRotation(double degrees)
12921 {
12922 mRotation = degrees;
12923 }
12924
12925 /*!
12926 Sets the distance between the border of the text rectangle and the text. The appearance (and
12927 visibility) of the text rectangle can be controlled with \ref setPen and \ref setBrush.
12928 */
12929 void QCPItemText::setPadding(const QMargins &padding)
12930 {
12931 mPadding = padding;
12932 }
12933
12934 /* inherits documentation from base class */
12935 double QCPItemText::selectTest(const QPointF &pos) const
12936 {
12937 if (!mVisible)
12938 return -1;
12939
12940 // The rect may be rotated, so we transform the actual clicked pos to the rotated
12941 // coordinate system, wo we can use the normal rectSelectTest function for non-rotated rects:
12942 QPointF positionPixels(position->pixelPoint());
12943 QTransform inputTransform;
12944 inputTransform.translate(positionPixels.x(), positionPixels.y());
12945 inputTransform.rotate(-mRotation);
12946 inputTransform.translate(-positionPixels.x(), -positionPixels.y());
12947 QPointF rotatedPos = inputTransform.map(pos);
12948 QFontMetrics fontMetrics(mFont);
12949 QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
12950 QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
12951 QPointF textPos = getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment);
12952 textBoxRect.moveTopLeft(textPos.toPoint());
12953
12954 return rectSelectTest(textBoxRect, rotatedPos, true);
12955 }
12956
12957 /* inherits documentation from base class */
12958 void QCPItemText::draw(QCPPainter *painter)
12959 {
12960 QPointF pos(position->pixelPoint());
12961 QTransform transform;
12962 transform.translate(pos.x(), pos.y());
12963 if (!qFuzzyIsNull(mRotation))
12964 transform.rotate(mRotation);
12965 painter->setFont(mainFont());
12966 QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
12967 QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
12968 QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
12969 textRect.moveTopLeft(textPos.toPoint()+QPoint(mPadding.left(), mPadding.top()));
12970 textBoxRect.moveTopLeft(textPos.toPoint());
12971 double clipPad = mainPen().widthF();
12972 QRect boundingRect = textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
12973 if (transform.mapRect(boundingRect).intersects(clipRect()))
12974 {
12975 painter->setTransform(transform);
12976 if ((mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) ||
12977 (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0))
12978 {
12979 painter->setPen(mainPen());
12980 painter->setBrush(mainBrush());
12981 painter->drawRect(textBoxRect);
12982 }
12983 painter->setBrush(Qt::NoBrush);
12984 painter->setPen(QPen(mainColor()));
12985 painter->drawText(textRect, Qt::TextDontClip|mTextAlignment, mText);
12986 }
12987 }
12988
12989 /* inherits documentation from base class */
12990 QPointF QCPItemText::anchorPixelPoint(int anchorId) const
12991 {
12992 // get actual rect points (pretty much copied from draw function):
12993 QPointF pos(position->pixelPoint());
12994 QTransform transform;
12995 transform.translate(pos.x(), pos.y());
12996 if (!qFuzzyIsNull(mRotation))
12997 transform.rotate(mRotation);
12998 QFontMetrics fontMetrics(mainFont());
12999 QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
13000 QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
13001 QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
13002 textBoxRect.moveTopLeft(textPos.toPoint());
13003 QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect));
13004
13005 switch (anchorId)
13006 {
13007 case aiTopLeft: return rectPoly.at(0);
13008 case aiTop: return (rectPoly.at(0)+rectPoly.at(1))*0.5;
13009 case aiTopRight: return rectPoly.at(1);
13010 case aiRight: return (rectPoly.at(1)+rectPoly.at(2))*0.5;
13011 case aiBottomRight: return rectPoly.at(2);
13012 case aiBottom: return (rectPoly.at(2)+rectPoly.at(3))*0.5;
13013 case aiBottomLeft: return rectPoly.at(3);
13014 case aiLeft: return (rectPoly.at(3)+rectPoly.at(0))*0.5;
13015 }
13016
13017 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
13018 return QPointF();
13019 }
13020
13021 /*! \internal
13022
13023 Returns the point that must be given to the QPainter::drawText function (which expects the top
13024 left point of the text rect), according to the position \a pos, the text bounding box \a rect and
13025 the requested \a positionAlignment.
13026
13027 For example, if \a positionAlignment is <tt>Qt::AlignLeft | Qt::AlignBottom</tt> the returned point
13028 will be shifted upward by the height of \a rect, starting from \a pos. So if the text is finally
13029 drawn at that point, the lower left corner of the resulting text rect is at \a pos.
13030 */
13031 QPointF QCPItemText::getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
13032 {
13033 if (positionAlignment == 0 || positionAlignment == (Qt::AlignLeft|Qt::AlignTop))
13034 return pos;
13035
13036 QPointF result = pos; // start at top left
13037 if (positionAlignment.testFlag(Qt::AlignHCenter))
13038 result.rx() -= rect.width()/2.0;
13039 else if (positionAlignment.testFlag(Qt::AlignRight))
13040 result.rx() -= rect.width();
13041 if (positionAlignment.testFlag(Qt::AlignVCenter))
13042 result.ry() -= rect.height()/2.0;
13043 else if (positionAlignment.testFlag(Qt::AlignBottom))
13044 result.ry() -= rect.height();
13045 return result;
13046 }
13047
13048 /*! \internal
13049
13050 Returns the font that should be used for drawing text. Returns mFont when the item is not selected
13051 and mSelectedFont when it is.
13052 */
13053 QFont QCPItemText::mainFont() const
13054 {
13055 return mSelected ? mSelectedFont : mFont;
13056 }
13057
13058 /*! \internal
13059
13060 Returns the color that should be used for drawing text. Returns mColor when the item is not
13061 selected and mSelectedColor when it is.
13062 */
13063 QColor QCPItemText::mainColor() const
13064 {
13065 return mSelected ? mSelectedColor : mColor;
13066 }
13067
13068 /*! \internal
13069
13070 Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
13071 and mSelectedPen when it is.
13072 */
13073 QPen QCPItemText::mainPen() const
13074 {
13075 return mSelected ? mSelectedPen : mPen;
13076 }
13077
13078 /*! \internal
13079
13080 Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
13081 is not selected and mSelectedBrush when it is.
13082 */
13083 QBrush QCPItemText::mainBrush() const
13084 {
13085 return mSelected ? mSelectedBrush : mBrush;
13086 }
13087
13088
13089 // ================================================================================
13090 // =================== QCPPainter
13091 // ================================================================================
13092
13093 /*! \class QCPPainter
13094 \brief QPainter subclass used internally
13095
13096 This internal class is used to provide some extended functionality e.g. for tweaking position
13097 consistency between antialiased and non-antialiased painting and drawing common shapes (like
13098 scatter symbols). Further it provides workarounds for QPainter quirks.
13099
13100 \warning This class intentionally hides non-virtual functions of QPainter, e.g. setPen, save and
13101 restore. So while it is possible to pass a QCPPainter instance to a function that expects a
13102 QPainter pointer, some of the workarounds and tweaks will be unavailable to the function (because
13103 it will call the base class implementations of the functions actually hidden by QCPPainter).
13104 */
13105
13106 /*!
13107 Creates a new QCPPainter instance and sets default values
13108 */
13109 QCPPainter::QCPPainter() :
13110 QPainter(),
13111 mScaledExportMode(false),
13112 mPdfExportMode(false),
13113 mIsAntialiasing(false)
13114 {
13115 }
13116
13117 /*!
13118 Creates a new QCPPainter instance on the specified paint \a device and sets default values. Just
13119 like the analogous QPainter constructor, begins painting on \a device immediately.
13120 */
13121 QCPPainter::QCPPainter(QPaintDevice *device) :
13122 QPainter(device),
13123 mScaledExportMode(false),
13124 mPdfExportMode(false),
13125 mIsAntialiasing(false)
13126 {
13127 }
13128
13129 QCPPainter::~QCPPainter()
13130 {
13131 }
13132
13133 /*!
13134 Sets the pixmap that will be used to draw scatters with \ref drawScatter, when the style is
13135 QCP::ssPixmap.
13136 */
13137 void QCPPainter::setScatterPixmap(const QPixmap pm)
13138 {
13139 mScatterPixmap = pm;
13140 }
13141
13142 /*!
13143 Sets the pen of the painter and applies certain fixes to it, depending on the mode of this
13144 QCPPainter.
13145
13146 \note this function hides the non-virtual base class implementation.
13147 */
13148 void QCPPainter::setPen(const QPen &pen)
13149 {
13150 QPainter::setPen(pen);
13151 if (mScaledExportMode)
13152 fixScaledPen();
13153 }
13154
13155 /*! \overload
13156
13157 Sets the pen (by color) of the painter and applies certain fixes to it, depending on the mode of
13158 this QCPPainter.
13159
13160 \note this function hides the non-virtual base class implementation.
13161 */
13162 void QCPPainter::setPen(const QColor &color)
13163 {
13164 QPainter::setPen(color);
13165 if (mScaledExportMode)
13166 fixScaledPen();
13167 }
13168
13169 /*! \overload
13170
13171 Sets the pen (by style) of the painter and applies certain fixes to it, depending on the mode of
13172 this QCPPainter.
13173
13174 \note this function hides the non-virtual base class implementation.
13175 */
13176 void QCPPainter::setPen(Qt::PenStyle penStyle)
13177 {
13178 QPainter::setPen(penStyle);
13179 if (mScaledExportMode)
13180 fixScaledPen();
13181 }
13182
13183 /*! \overload
13184
13185 Works around a Qt bug introduced with Qt 4.8 which makes drawing QLineF unpredictable when
13186 antialiasing is disabled.
13187
13188 \note this function hides the non-virtual base class implementation.
13189 */
13190 void QCPPainter::drawLine(const QLineF &line)
13191 {
13192 if (mIsAntialiasing)
13193 QPainter::drawLine(line);
13194 else
13195 QPainter::drawLine(line.toLine());
13196 }
13197
13198 /*!
13199 Sets whether painting uses antialiasing or not. Use this method instead of using setRenderHint
13200 with QPainter::Antialiasing directly, as it allows QCPPainter to regain pixel exactness between
13201 antialiased and non-antialiased painting (Since Qt uses slightly different coordinate systems for
13202 AA/Non-AA painting).
13203 */
13204 void QCPPainter::setAntialiasing(bool enabled)
13205 {
13206 if (mPdfExportMode)
13207 return;
13208
13209 setRenderHint(QPainter::Antialiasing, enabled);
13210 if (mIsAntialiasing != enabled)
13211 {
13212 if (mIsAntialiasing)
13213 translate(-0.5, -0.5);
13214 else
13215 translate(0.5, 0.5);
13216 mIsAntialiasing = enabled;
13217 }
13218 }
13219
13220 /*!
13221 Saves the painter (see QPainter::save). Since QCPPainter adds some new internal state to
13222 QPainter, the save/restore functions are reimplemented to also save/restore those members.
13223
13224 \note this function hides the non-virtual base class implementation.
13225
13226 \see restore
13227 */
13228 void QCPPainter::save()
13229 {
13230 mAntialiasingStack.push(mIsAntialiasing);
13231 QPainter::save();
13232 }
13233
13234 /*!
13235 Restores the painter (see QPainter::restore). Since QCPPainter adds some new internal state to
13236 QPainter, the save/restore functions are reimplemented to also save/restore those members.
13237
13238 \note this function hides the non-virtual base class implementation.
13239
13240 \see save
13241 */
13242 void QCPPainter::restore()
13243 {
13244 if (!mAntialiasingStack.isEmpty())
13245 mIsAntialiasing = mAntialiasingStack.pop();
13246 else
13247 qDebug() << Q_FUNC_INFO << "Unbalanced save/restore";
13248 QPainter::restore();
13249 }
13250
13251 /*!
13252 Sets whether the painter shall adjust its fixes/workarounds optimized for vectorized pdf export.
13253
13254 This means for example, that the antialiasing/non-antialiasing fix introduced with \ref
13255 setAntialiasing is not used, since PDF is not rastered and thus works with floating point data
13256 natively.
13257 */
13258 void QCPPainter::setPdfExportMode(bool enabled)
13259 {
13260 mPdfExportMode = enabled;
13261 }
13262
13263 /*!
13264 Sets whether the painter shall adjust its fixes/workarounds optimized for scaled export to
13265 rastered image formats.
13266
13267 For example this provides a workaround for a QPainter bug that prevents scaling of pen widths for
13268 pens with width 0, although the QPainter::NonCosmeticDefaultPen render hint is set.
13269 */
13270 void QCPPainter::setScaledExportMode(bool enabled)
13271 {
13272 mScaledExportMode = enabled;
13273 }
13274
13275 /*!
13276 Provides a workaround for a QPainter bug that prevents scaling of pen widths for pens with width
13277 0, although the QPainter::NonCosmeticDefaultPen render hint is set.
13278
13279 Changes the pen width from 0 to 1, if appropriate.
13280
13281 Does nothing if the QCPPainter is not in scaled export mode (\ref setScaledExportMode).
13282 */
13283 void QCPPainter::fixScaledPen()
13284 {
13285 if (mScaledExportMode && pen().isCosmetic() && qFuzzyIsNull(pen().widthF()))
13286 {
13287 QPen p = pen();
13288 p.setWidth(1);
13289 QPainter::setPen(p);
13290 }
13291 }
13292
13293 /*!
13294 Draws a single scatter point with the specified \a style and \a size in pixels at the pixel position \a x and \a y.
13295
13296 If the \a style is ssPixmap, make sure to pass the respective pixmap with \ref setScatterPixmap before calling
13297 this function.
13298 */
13299 void QCPPainter::drawScatter(double x, double y, double size, QCP::ScatterStyle style)
13300 {
13301 double w = size/2.0;
13302 switch (style)
13303 {
13304 case QCP::ssNone: break;
13305 case QCP::ssDot:
13306 {
13307 drawPoint(QPointF(x, y));
13308 break;
13309 }
13310 case QCP::ssCross:
13311 {
13312 drawLine(QLineF(x-w, y-w, x+w, y+w));
13313 drawLine(QLineF(x-w, y+w, x+w, y-w));
13314 break;
13315 }
13316 case QCP::ssPlus:
13317 {
13318 drawLine(QLineF(x-w, y, x+w, y));
13319 drawLine(QLineF(x, y+w, x, y-w));
13320 break;
13321 }
13322 case QCP::ssCircle:
13323 {
13324 setBrush(Qt::NoBrush);
13325 drawEllipse(QPointF(x,y), w, w);
13326 break;
13327 }
13328 case QCP::ssDisc:
13329 {
13330 setBrush(QBrush(pen().color()));
13331 drawEllipse(QPointF(x,y), w, w);
13332 break;
13333 }
13334 case QCP::ssSquare:
13335 {
13336 setBrush(Qt::NoBrush);
13337 drawRect(QRectF(x-w, y-w, size, size));
13338 break;
13339 }
13340 case QCP::ssDiamond:
13341 {
13342 setBrush(Qt::NoBrush);
13343 drawLine(QLineF(x-w, y, x, y-w));
13344 drawLine(QLineF(x, y-w, x+w, y));
13345 drawLine(QLineF(x+w, y, x, y+w));
13346 drawLine(QLineF(x, y+w, x-w, y));
13347 break;
13348 }
13349 case QCP::ssStar:
13350 {
13351 drawLine(QLineF(x-w, y, x+w, y));
13352 drawLine(QLineF(x, y+w, x, y-w));
13353 drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.707, y+w*0.707));
13354 drawLine(QLineF(x-w*0.707, y+w*0.707, x+w*0.707, y-w*0.707));
13355 break;
13356 }
13357 case QCP::ssTriangle:
13358 {
13359 drawLine(QLineF(x-w, y+0.755*w, x+w, y+0.755*w));
13360 drawLine(QLineF(x+w, y+0.755*w, x, y-0.977*w));
13361 drawLine(QLineF(x, y-0.977*w, x-w, y+0.755*w));
13362 break;
13363 }
13364 case QCP::ssTriangleInverted:
13365 {
13366 drawLine(QLineF(x-w, y-0.755*w, x+w, y-0.755*w));
13367 drawLine(QLineF(x+w, y-0.755*w, x, y+0.977*w));
13368 drawLine(QLineF(x, y+0.977*w, x-w, y-0.755*w));
13369 break;
13370 }
13371 case QCP::ssCrossSquare:
13372 {
13373 setBrush(Qt::NoBrush);
13374 drawLine(QLineF(x-w, y-w, x+w*0.95, y+w*0.95));
13375 drawLine(QLineF(x-w, y+w*0.95, x+w*0.95, y-w));
13376 drawRect(QRectF(x-w,y-w,size,size));
13377 break;
13378 }
13379 case QCP::ssPlusSquare:
13380 {
13381 setBrush(Qt::NoBrush);
13382 drawLine(QLineF(x-w, y, x+w*0.95, y));
13383 drawLine(QLineF(x, y+w, x, y-w));
13384 drawRect(QRectF(x-w, y-w, size, size));
13385 break;
13386 }
13387 case QCP::ssCrossCircle:
13388 {
13389 setBrush(Qt::NoBrush);
13390 drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.67, y+w*0.67));
13391 drawLine(QLineF(x-w*0.707, y+w*0.67, x+w*0.67, y-w*0.707));
13392 drawEllipse(QPointF(x,y), w, w);
13393 break;
13394 }
13395 case QCP::ssPlusCircle:
13396 {
13397 setBrush(Qt::NoBrush);
13398 drawLine(QLineF(x-w, y, x+w, y));
13399 drawLine(QLineF(x, y+w, x, y-w));
13400 drawEllipse(QPointF(x,y), w, w);
13401 break;
13402 }
13403 case QCP::ssPeace:
13404 {
13405 setBrush(Qt::NoBrush);
13406 drawLine(QLineF(x, y-w, x, y+w));
13407 drawLine(QLineF(x, y, x-w*0.707, y+w*0.707));
13408 drawLine(QLineF(x, y, x+w*0.707, y+w*0.707));
13409 drawEllipse(QPointF(x,y), w, w);
13410 break;
13411 }
13412 case QCP::ssPixmap:
13413 {
13414 drawPixmap(x-mScatterPixmap.width()*0.5, y-mScatterPixmap.height()*0.5, mScatterPixmap);
13415 // if something in here is changed, adapt QCP::ssPixmap special case in drawLegendIcon(), too
13416 break;
13417 }
13418 }
13419 }
13420
13421
13422 // ================================================================================
13423 // =================== QCPLineEnding
13424 // ================================================================================
13425
13426 /*! \class QCPLineEnding
13427 \brief Handles the different ending decorations for line-like items
13428
13429 \image html QCPLineEnding.png "The various ending styles currently supported"
13430
13431 For every ending a line-like item has, an instance of this class exists. For example, QCPItemLine
13432 has two endings which can be set with QCPItemLine::setHead and QCPItemLine::setTail.
13433
13434 The styles themselves are defined via the enum QCPLineEnding::EndingStyle. Most decorations can
13435 be modified regarding width and length, see \ref setWidth and \ref setLength. The direction of
13436 the ending decoration (e.g. direction an arrow is pointing) is controlled by the line-like item.
13437 For example, when both endings of a QCPItemLine are set to be arrows, they will point to opposite
13438 directions, e.g. "outward". This can be changed by \ref setInverted, which would make the
13439 respective arrow point inward.
13440
13441 Note that due to the overloaded QCPLineEnding constructor, you may directly specify a
13442 QCPLineEnding::EndingStyle where actually a QCPLineEnding is expected, e.g. \code
13443 myItemLine->setHead(QCPLineEnding::esSpikeArrow) \endcode
13444 */
13445
13446 /*!
13447 Creates a QCPLineEnding instance with default values (style \ref esNone).
13448 */
13449 QCPLineEnding::QCPLineEnding() :
13450 mStyle(esNone),
13451 mWidth(8),
13452 mLength(10),
13453 mInverted(false)
13454 {
13455 }
13456
13457 /*!
13458 Creates a QCPLineEnding instance with the specified values.
13459 */
13460 QCPLineEnding::QCPLineEnding(QCPLineEnding::EndingStyle style, double width, double length, bool inverted) :
13461 mStyle(style),
13462 mWidth(width),
13463 mLength(length),
13464 mInverted(inverted)
13465 {
13466 }
13467
13468 /*!
13469 Sets the style of the ending decoration.
13470 */
13471 void QCPLineEnding::setStyle(QCPLineEnding::EndingStyle style)
13472 {
13473 mStyle = style;
13474 }
13475
13476 /*!
13477 Sets the width of the ending decoration, if the style supports it. On arrows, for example, the
13478 width defines the size perpendicular to the arrow's pointing direction.
13479
13480 \see setLength
13481 */
13482 void QCPLineEnding::setWidth(double width)
13483 {
13484 mWidth = width;
13485 }
13486
13487 /*!
13488 Sets the length of the ending decoration, if the style supports it. On arrows, for example, the
13489 length defines the size in pointing direction.
13490
13491 \see setWidth
13492 */
13493 void QCPLineEnding::setLength(double length)
13494 {
13495 mLength = length;
13496 }
13497
13498 /*!
13499 Sets whether the direction of the ending decoration shall be inverted with respect to the natural
13500 direction given by the parent item. For example, an arrow decoration will point inward when
13501 \a inverted is set to true.
13502 */
13503 void QCPLineEnding::setInverted(bool inverted)
13504 {
13505 mInverted = inverted;
13506 }
13507
13508 /*! \internal
13509
13510 Returns the maximum pixel radius the ending decoration might cover, starting from the position
13511 the decoration is drawn at (typically a line ending/\ref QCPItemPosition of an item).
13512
13513 This is relevant for clipping. Only omit painting of the decoration when the position where the
13514 decoration is supposed to be drawn is farther away from the clipping rect than the returned
13515 distance.
13516 */
13517 double QCPLineEnding::boundingDistance() const
13518 {
13519 switch (mStyle)
13520 {
13521 case esNone:
13522 return 0;
13523
13524 case esFlatArrow:
13525 case esSpikeArrow:
13526 case esLineArrow:
13527 return qSqrt(mWidth*mWidth+mLength*mLength); // items that have width and length
13528
13529 case esDisc:
13530 case esSquare:
13531 case esDiamond:
13532 case esBar:
13533 return mWidth*1.42; // items that only have a width -> with*sqrt(2)
13534 }
13535 return 0;
13536 }
13537
13538 /*! \internal
13539
13540 Draws the line ending with the specified \a painter at the position \a pos. The direction of the
13541 line ending is controlled with \a dir.
13542 */
13543 void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const
13544 {
13545 if (mStyle == esNone)
13546 return;
13547
13548 QVector2D lengthVec(dir.normalized()*(mInverted ? -1 : 1));
13549 if (lengthVec.isNull())
13550 lengthVec = QVector2D(1, 0);
13551 QVector2D widthVec(-lengthVec.y(), lengthVec.x());
13552 lengthVec *= mLength;
13553 widthVec *= mWidth*0.5;
13554
13555 QPen penBackup = painter->pen();
13556 QPen miterPen = penBackup;
13557 miterPen.setJoinStyle(Qt::MiterJoin);
13558 switch (mStyle)
13559 {
13560 case esNone: break;
13561 case esFlatArrow:
13562 {
13563 QPointF points[3] = {pos.toPointF(),
13564 (pos-lengthVec+widthVec).toPointF(),
13565 (pos-lengthVec-widthVec).toPointF()
13566 };
13567 painter->setPen(miterPen);
13568 painter->drawConvexPolygon(points, 3);
13569 painter->setPen(penBackup);
13570 break;
13571 }
13572 case esSpikeArrow:
13573 {
13574 QPointF points[4] = {pos.toPointF(),
13575 (pos-lengthVec+widthVec).toPointF(),
13576 (pos-lengthVec*0.8).toPointF(),
13577 (pos-lengthVec-widthVec).toPointF()
13578 };
13579 painter->setPen(miterPen);
13580 painter->drawConvexPolygon(points, 4);
13581 painter->setPen(penBackup);
13582 break;
13583 }
13584 case esLineArrow:
13585 {
13586 QPointF points[3] = {(pos-lengthVec+widthVec).toPointF(),
13587 pos.toPointF(),
13588 (pos-lengthVec-widthVec).toPointF()
13589 };
13590 painter->setPen(miterPen);
13591 painter->drawPolyline(points, 3);
13592 painter->setPen(penBackup);
13593 break;
13594 }
13595 case esDisc:
13596 {
13597 painter->drawEllipse(pos.toPointF(), mWidth*0.5, mWidth*0.5);
13598 break;
13599 }
13600 case esSquare:
13601 {
13602 QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
13603 QPointF points[4] = {(pos-widthVecPerp+widthVec).toPointF(),
13604 (pos-widthVecPerp-widthVec).toPointF(),
13605 (pos+widthVecPerp-widthVec).toPointF(),
13606 (pos+widthVecPerp+widthVec).toPointF()
13607 };
13608 painter->setPen(miterPen);
13609 painter->drawConvexPolygon(points, 4);
13610 painter->setPen(penBackup);
13611 break;
13612 }
13613 case esDiamond:
13614 {
13615 QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
13616 QPointF points[4] = {(pos-widthVecPerp).toPointF(),
13617 (pos-widthVec).toPointF(),
13618 (pos+widthVecPerp).toPointF(),
13619 (pos+widthVec).toPointF()
13620 };
13621 painter->setPen(miterPen);
13622 painter->drawConvexPolygon(points, 4);
13623 painter->setPen(penBackup);
13624 break;
13625 }
13626 case esBar:
13627 {
13628 painter->drawLine((pos+widthVec).toPointF(), (pos-widthVec).toPointF());
13629 break;
13630 }
13631 }
13632 }
13633
13634 /*! \internal
13635 \overload
13636
13637 Draws the line ending. The direction is controlled with the \a angle parameter in radians.
13638 */
13639 void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, double angle) const
13640 {
13641 draw(painter, pos, QVector2D(qCos(angle), qSin(angle)));
13642 }
13643
13644
13645 // ================================================================================
13646 // =================== QCPItemCurve
13647 // ================================================================================
13648
13649 /*! \class QCPItemCurve
13650 \brief A curved line from one point to another
13651
13652 \image html QCPItemCurve.png "Curve example. Blue dotted circles are anchors, solid blue discs are positions."
13653
13654 It has four positions, \a start and \a end, which define the end points of the line, and two
13655 control points which define the direction the line exits from the start and the direction from
13656 which it approaches the end: \a startDir and \a endDir.
13657
13658 With \ref setHead and \ref setTail you may set different line ending styles, e.g. to create an
13659 arrow.
13660
13661 Often it is desirable for the control points to stay at fixed relative positions to the start/end
13662 point. This can be achieved by setting the parent anchor e.g. of \a startDir simply to \a start,
13663 and then specify the desired pixel offset with QCPItemPosition::setCoords on \a startDir.
13664 */
13665
13666 /*!
13667 Creates a curve item and sets default values.
13668
13669 The constructed item can be added to the plot with QCustomPlot::addItem.
13670 */
13671 QCPItemCurve::QCPItemCurve(QCustomPlot *parentPlot) :
13672 QCPAbstractItem(parentPlot),
13673 start(createPosition("start")),
13674 startDir(createPosition("startDir")),
13675 endDir(createPosition("endDir")),
13676 end(createPosition("end"))
13677 {
13678 start->setCoords(0, 0);
13679 startDir->setCoords(0.5, 0);
13680 endDir->setCoords(0, 0.5);
13681 end->setCoords(1, 1);
13682
13683 setPen(QPen(Qt::black));
13684 setSelectedPen(QPen(Qt::blue,2));
13685 }
13686
13687 QCPItemCurve::~QCPItemCurve()
13688 {
13689 }
13690
13691 /*!
13692 Sets the pen that will be used to draw the line
13693
13694 \see setSelectedPen
13695 */
13696 void QCPItemCurve::setPen(const QPen &pen)
13697 {
13698 mPen = pen;
13699 }
13700
13701 /*!
13702 Sets the pen that will be used to draw the line when selected
13703
13704 \see setPen, setSelected
13705 */
13706 void QCPItemCurve::setSelectedPen(const QPen &pen)
13707 {
13708 mSelectedPen = pen;
13709 }
13710
13711 /*!
13712 Sets the line ending style of the head. The head corresponds to the \a end position.
13713
13714 Note that due to the overloaded QCPLineEnding constructor, you may directly specify
13715 a QCPLineEnding::EndingStyle here, e.g. \code setHead(QCPLineEnding::esSpikeArrow) \endcode
13716
13717 \see setTail
13718 */
13719 void QCPItemCurve::setHead(const QCPLineEnding &head)
13720 {
13721 mHead = head;
13722 }
13723
13724 /*!
13725 Sets the line ending style of the tail. The tail corresponds to the \a start position.
13726
13727 Note that due to the overloaded QCPLineEnding constructor, you may directly specify
13728 a QCPLineEnding::EndingStyle here, e.g. \code setTail(QCPLineEnding::esSpikeArrow) \endcode
13729
13730 \see setHead
13731 */
13732 void QCPItemCurve::setTail(const QCPLineEnding &tail)
13733 {
13734 mTail = tail;
13735 }
13736
13737 /* inherits documentation from base class */
13738 double QCPItemCurve::selectTest(const QPointF &pos) const
13739 {
13740 if (!mVisible)
13741 return -1;
13742
13743 QPointF startVec(start->pixelPoint());
13744 QPointF startDirVec(startDir->pixelPoint());
13745 QPointF endDirVec(endDir->pixelPoint());
13746 QPointF endVec(end->pixelPoint());
13747
13748 QPainterPath cubicPath(startVec);
13749 cubicPath.cubicTo(startDirVec, endDirVec, endVec);
13750
13751 QPolygonF polygon = cubicPath.toSubpathPolygons().first();
13752 double minDistSqr = std::numeric_limits<double>::max();
13753 for (int i=1; i<polygon.size(); ++i)
13754 {
13755 double distSqr = distSqrToLine(polygon.at(i-1), polygon.at(i), pos);
13756 if (distSqr < minDistSqr)
13757 minDistSqr = distSqr;
13758 }
13759 return qSqrt(minDistSqr);
13760 }
13761
13762 /* inherits documentation from base class */
13763 void QCPItemCurve::draw(QCPPainter *painter)
13764 {
13765 QPointF startVec(start->pixelPoint());
13766 QPointF startDirVec(startDir->pixelPoint());
13767 QPointF endDirVec(endDir->pixelPoint());
13768 QPointF endVec(end->pixelPoint());
13769 if (QVector2D(endVec-startVec).length() > 1e10) // too large curves cause crash
13770 return;
13771
13772 QPainterPath cubicPath(startVec);
13773 cubicPath.cubicTo(startDirVec, endDirVec, endVec);
13774
13775 // paint visible segment, if existent:
13776 QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
13777 QRect cubicRect = cubicPath.controlPointRect().toRect();
13778 if (cubicRect.isEmpty()) // may happen when start and end exactly on same x or y position
13779 cubicRect.adjust(0, 0, 1, 1);
13780 if (clip.intersects(cubicRect))
13781 {
13782 painter->setPen(mainPen());
13783 painter->drawPath(cubicPath);
13784 painter->setBrush(Qt::SolidPattern);
13785 if (mTail.style() != QCPLineEnding::esNone)
13786 mTail.draw(painter, QVector2D(startVec), M_PI-cubicPath.angleAtPercent(0)/180.0*M_PI);
13787 if (mHead.style() != QCPLineEnding::esNone)
13788 mHead.draw(painter, QVector2D(endVec), -cubicPath.angleAtPercent(1)/180.0*M_PI);
13789 }
13790 }
13791
13792 /*! \internal
13793
13794 Returns the pen that should be used for drawing lines. Returns mPen when the
13795 item is not selected and mSelectedPen when it is.
13796 */
13797 QPen QCPItemCurve::mainPen() const
13798 {
13799 return mSelected ? mSelectedPen : mPen;
13800 }
13801
13802
13803 // ================================================================================
13804 // =================== QCPLayer
13805 // ================================================================================
13806
13807 /*! \class QCPLayer
13808 \brief A layer that may contain objects, to control the rendering order
13809
13810 The Layering system of QCustomPlot is the mechanism to control the rendering order of the
13811 elements inside the plot, e.g. that the grid is drawn behind plottables etc.
13812
13813 It is based on the two classes QCPLayer and QCPLayerable. A QCustomPlot contains an ordered list
13814 of one or more instances of QCPLayer (see QCustomPlot::addLayer, QCustomPlot::layer,
13815 QCustomPlot::moveLayer, etc.). The layers are drawn in the order they are in the list.
13816
13817 A QCPLayer itself contains an ordered list of QCPLayerable instances. QCPLayerable is an abstract
13818 base class from which almost all visible objects derive, like axes, grids, graphs, items, etc.
13819
13820 By default, QCustomPlot has three layers: "grid", "main" and "axes" (in that order). Initially
13821 the QCPGrid instances are on the "grid" layer, so the grid will be drawn beneath the objects on
13822 the other two layers. The top layer is "axes" and contains all four axes, so they will be drawn
13823 on top. Between these two layers, there is the "main" layer. It is initially empty and set as the
13824 current layer (see QCustomPlot::setCurrentLayer). This means, all new plottables, items etc.
13825 are created on this layer by default, and are thus drawn above the grid but below the axes.
13826
13827 Controlling the ordering of objects is easy: Create a new layer in the position you want it to
13828 be, e.g. above "main", with QCustomPlot::addLayer. Then set the current layer with
13829 QCustomPlot::setCurrentLayer to that new layer and finally create the objects normally. They will
13830 be placed on the new layer automatically, due to the current layer setting. Alternatively you
13831 could have also ignored the current layer setting and just moved the objects with
13832 QCPLayerable::setLayer to the desired layer after creating them.
13833
13834 It is also possible to move whole layers. For example, If you want the grid to be shown in front
13835 of all plottables/items on the "main" layer, just move it above "main" with
13836 QCustomPlot::moveLayer. This way the ordering might now be "main", "grid", "axes", so while the
13837 grid will still be beneath the axes, it will now be drawn above plottables/items on "main", as
13838 intended.
13839
13840 The rendering order within one layer is simply by order of creation. The item created last (or
13841 added last to the layer), is drawn on top of all other objects on that layer.
13842
13843 When a layer is deleted, the objects on it are not deleted with it, but fall on the layer below
13844 the deleted layer, see QCustomPlot::removeLayer.
13845 */
13846
13847 /* start documentation of inline functions */
13848
13849 /*! \fn QList<QCPLayerable*> QCPLayer::children() const
13850
13851 Returns a list of all layerables on this layer. The order corresponds to the rendering order,
13852 i.e. layerables with higher indices are drawn above layerables with lower indices.
13853 */
13854
13855 /* end documentation of inline functions */
13856
13857 /*!
13858 Creates a new QCPLayer instance.
13859
13860 Normally you shouldn't directly create layers like this, use QCustomPlot::addLayer instead.
13861
13862 \warning It is not checked that \a layerName is actually an unique layer name in \a parentPlot.
13863 This check is only performed by QCustomPlot::addLayer.
13864 */
13865 QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName) :
13866 mParentPlot(parentPlot),
13867 mName(layerName)
13868 {
13869 // Note: no need to make sure layerName doesn't already, because layer
13870 // management is done with QCustomPlot functions.
13871 }
13872
13873 QCPLayer::~QCPLayer()
13874 {
13875 }
13876
13877 /*!
13878 Returns the index this layer has in the QCustomPlot. The index is the integer number by which this layer can be
13879 accessed via QCustomPlot::layer.
13880
13881 Layers with greater indices will be drawn above layers with smaller indices.
13882 */
13883 int QCPLayer::index() const
13884 {
13885 return mParentPlot->mLayers.indexOf(const_cast<QCPLayer*>(this));
13886 }
13887
13888 /*! \internal
13889
13890 Adds the \a layerable to the list of this layer. If \a prepend is set to true, the layerable will
13891 be prepended to the list, i.e. be drawn beneath the other layerables already in the list.
13892
13893 This function does not change the \a mLayer member of \a layerable to this layer. (Use
13894 QCPLayerable::setLayer to change the layer of an object, not this function.)
13895
13896 \see removeChild
13897 */
13898 void QCPLayer::addChild(QCPLayerable *layerable, bool prepend)
13899 {
13900 if (!mChildren.contains(layerable))
13901 {
13902 if (prepend)
13903 mChildren.prepend(layerable);
13904 else
13905 mChildren.append(layerable);
13906 } else
13907 qDebug() << Q_FUNC_INFO << "layerable is already child of this layer" << reinterpret_cast<quintptr>(layerable);
13908 }
13909
13910 /*! \internal
13911
13912 Removes the \a layerable from the list of this layer.
13913
13914 This function does not change the \a mLayer member of \a layerable. (Use QCPLayerable::setLayer
13915 to change the layer of an object, not this function.)
13916
13917 \see addChild
13918 */
13919 void QCPLayer::removeChild(QCPLayerable *layerable)
13920 {
13921 if (!mChildren.removeOne(layerable))
13922 qDebug() << Q_FUNC_INFO << "layerable is not child of this layer" << reinterpret_cast<quintptr>(layerable);
13923 }
13924
13925
13926 // ================================================================================
13927 // =================== QCPLayerable
13928 // ================================================================================
13929
13930 /*! \class QCPLayerable
13931 \brief Base class for all objects that can be placed on layers
13932
13933 This is the abstract base class most visible objects derive from, e.g. plottables, axes, grid
13934 etc.
13935
13936 Every layerable is on a layer (QCPLayer) which allows controlling the rendering order by stacking
13937 the layers accordingly.
13938
13939 For details about the layering mechanism, see the QCPLayer documentation.
13940 */
13941
13942 /* start documentation of pure virtual functions */
13943
13944 /*! \fn virtual void QCPLayerable::applyDefaultAntialiasingHint(QCPPainter *painter) const = 0
13945 \internal
13946
13947 This function applies the default antialiasing setting to the specified \a painter, using the
13948 function \ref applyAntialiasingHint. This is the antialiasing state the painter is in, when \ref
13949 draw is called on the layerable. If the layerable has multiple entities whose antialiasing
13950 setting may be specified individually, this function should set the antialiasing state of the
13951 most prominent entity. In this case however, the \ref draw function usually calls the specialized
13952 versions of this function before drawing each entity, effectively overriding the setting of the
13953 default antialiasing hint.
13954
13955 <b>First example:</b> QCPGraph has multiple entities that have an antialiasing setting: The graph
13956 line, fills, scatters and error bars. Those can be configured via QCPGraph::setAntialiased,
13957 QCPGraph::setAntialiasedFill, QCPGraph::setAntialiasedScatters etc. Consequently, there isn't
13958 only the QCPGraph::applyDefaultAntialiasingHint function (which corresponds to the graph line's
13959 antialiasing), but specialized ones like QCPGraph::applyFillAntialiasingHint and
13960 QCPGraph::applyScattersAntialiasingHint. So before drawing one of those entities, QCPGraph::draw
13961 calls the respective specialized applyAntialiasingHint function.
13962
13963 <b>Second example:</b> QCPItemLine consists only of a line so there is only one antialiasing
13964 setting which can be controlled with QCPItemLine::setAntialiased. (This function is inherited by
13965 all layerables. The specialized functions, as seen on QCPGraph, must be added explicitly to the
13966 respective layerable subclass.) Consequently it only has the normal
13967 QCPItemLine::applyDefaultAntialiasingHint. The \ref QCPItemLine::draw function doesn't need to
13968 care about setting any antialiasing states, because the default antialiasing hint is already set
13969 on the painter when the \ref draw function is called, and that's the state it wants to draw the
13970 line with.
13971 */
13972
13973 /*! \fn virtual void QCPLayerable::draw(QCPPainter *painter) const = 0
13974 \internal
13975
13976 This function draws the layerable to the specified \a painter.
13977
13978 Before this function is called, the painter's antialiasing state is set via \ref
13979 applyDefaultAntialiasingHint, see the documentation there. Further, its clipping rectangle was
13980 set to \ref clipRect.
13981 */
13982
13983 /* end documentation of pure virtual functions */
13984
13985 /*!
13986 Creates a new QCPLayerable instance.
13987
13988 Since QCPLayerable is an abstract base class, it can't be instantiated directly. Use one of the
13989 derived classes.
13990 */
13991 QCPLayerable::QCPLayerable(QCustomPlot *parentPlot) :
13992 QObject(0), // rather not bind to parentPlot, incase we want to allow moving of objects between customplots some day
13993 mVisible(true),
13994 mParentPlot(parentPlot),
13995 mLayer(0),
13996 mAntialiased(true)
13997 {
13998 if (mParentPlot)
13999 setLayer(mParentPlot->currentLayer());
14000 }
14001
14002 QCPLayerable::~QCPLayerable()
14003 {
14004 if (mLayer)
14005 {
14006 mLayer->removeChild(this);
14007 mLayer = 0;
14008 }
14009 }
14010
14011 /*!
14012 Sets the visibility of this layerable object. If an object is not visible, it will not be drawn
14013 on the QCustomPlot surface, and user interaction with it (e.g. click/selection) is not possible.
14014 */
14015 void QCPLayerable::setVisible(bool on)
14016 {
14017 mVisible = on;
14018 }
14019
14020 /*!
14021 Sets the \a layer of this layerable object. The object will be placed on top of the other objects
14022 already on \a layer.
14023
14024 Returns true on success, i.e. if \a layer is a valid layer.
14025 */
14026 bool QCPLayerable::setLayer(QCPLayer *layer)
14027 {
14028 return moveToLayer(layer, false);
14029 }
14030
14031 /*! \overload
14032 Sets the layer of this layerable object by name
14033
14034 Returns true on success, i.e. if \a layerName is a valid layer name.
14035 */
14036 bool QCPLayerable::setLayer(const QString &layerName)
14037 {
14038 if (!mParentPlot)
14039 {
14040 qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
14041 return false;
14042 }
14043 if (QCPLayer *layer = mParentPlot->layer(layerName))
14044 {
14045 return setLayer(layer);
14046 } else
14047 {
14048 qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName;
14049 return false;
14050 }
14051 }
14052
14053 /*!
14054 Sets whether this object will be drawn antialiased or not.
14055
14056 Note that antialiasing settings may be overridden by QCustomPlot::setAntialiasedElements and
14057 QCustomPlot::setNotAntialiasedElements.
14058 */
14059 void QCPLayerable::setAntialiased(bool enabled)
14060 {
14061 mAntialiased = enabled;
14062 }
14063
14064 /*! \internal
14065
14066 Moves this layerable object to \a layer. If \a prepend is true, this object will be prepended to
14067 the new layer's list, i.e. it will be drawn below the objects already on the layer. If it is
14068 false, the object will be appended.
14069
14070 Returns true on success, i.e. if \a layer is a valid layer.
14071 */
14072 bool QCPLayerable::moveToLayer(QCPLayer *layer, bool prepend)
14073 {
14074 if (!mParentPlot)
14075 {
14076 qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
14077 return false;
14078 }
14079 if (layer && layer->parentPlot() != mParentPlot)
14080 {
14081 qDebug() << Q_FUNC_INFO << "layer" << layer->name() << "is not in same QCustomPlot as this layerable";
14082 return false;
14083 }
14084
14085 if (mLayer)
14086 mLayer->removeChild(this);
14087 mLayer = layer;
14088 if (mLayer)
14089 mLayer->addChild(this, prepend);
14090 return true;
14091 }
14092
14093 /*! \internal
14094
14095 Sets the QPainter::Antialiasing render hint on the provided \a painter, depending on the
14096 \a localAntialiased value as well as the overrides \ref QCustomPlot::setAntialiasedElements and
14097 \ref QCustomPlot::setNotAntialiasedElements. Which override enum this function takes into account is
14098 controlled via \a overrideElement.
14099 */
14100 void QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
14101 {
14102 if (mParentPlot && mParentPlot->notAntialiasedElements().testFlag(overrideElement))
14103 painter->setAntialiasing(false);
14104 else if (mParentPlot && mParentPlot->antialiasedElements().testFlag(overrideElement))
14105 painter->setAntialiasing(true);
14106 else
14107 painter->setAntialiasing(localAntialiased);
14108 }
14109
14110 /*! \internal
14111
14112 Returns the clipping rectangle of this layerable object. By default, this is the viewport of the parent QCustomPlot.
14113 Specific subclasses may reimplement this function to provide different clipping rects.
14114
14115 The returned clipping rect is set on the painter before the draw function of the respective
14116 object is called.
14117 */
14118 QRect QCPLayerable::clipRect() const
14119 {
14120 if (mParentPlot)
14121 return mParentPlot->viewport();
14122 else
14123 return QRect();
14124 }
14125
14126
14127 // ================================================================================
14128 // =================== QCPGrid
14129 // ================================================================================
14130
14131 /*! \class QCPGrid
14132 \brief Responsible for drawing the grid of a QCPAxis.
14133
14134 This class is tightly bound to QCPAxis. Every axis owns a grid instance internally and uses it to
14135 draw the grid. Normally, you don't need to interact with the QCPGrid instance, because QCPAxis
14136 reproduces the grid interface in its own interface.
14137
14138 The axis and grid drawing was split into two classes to allow them to be placed on different
14139 layers (both QCPAxis and QCPGrid inherit from QCPLayerable). So it is possible to have the grid
14140 at the background and the axes in the foreground, and any plottables/items in between. This
14141 described situation is the default setup, see QCPLayer documentation.
14142 */
14143
14144 /*!
14145 Creates a QCPGrid instance and sets default values.
14146
14147 You shouldn't instantiate grids on their own, since every QCPAxis brings its own QCPGrid
14148 internally
14149 */
14150 QCPGrid::QCPGrid(QCPAxis *parentAxis) :
14151 QCPLayerable(parentAxis->parentPlot()),
14152 mParentAxis(parentAxis)
14153 {
14154 setPen(QPen(QColor(200,200,200), 0, Qt::DotLine));
14155 setSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));
14156 setZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine));
14157 setSubGridVisible(false);
14158 setAntialiased(false);
14159 setAntialiasedSubGrid(false);
14160 setAntialiasedZeroLine(false);
14161 }
14162
14163 QCPGrid::~QCPGrid()
14164 {
14165 }
14166
14167 /*!
14168 Sets whether grid lines at sub tick marks are drawn.
14169
14170 \see setSubGridPen
14171 */
14172 void QCPGrid::setSubGridVisible(bool visible)
14173 {
14174 mSubGridVisible = visible;
14175 }
14176
14177 /*!
14178 Sets whether sub grid lines are drawn antialiased.
14179 */
14180 void QCPGrid::setAntialiasedSubGrid(bool enabled)
14181 {
14182 mAntialiasedSubGrid = enabled;
14183 }
14184
14185 /*!
14186 Sets whether zero lines are drawn antialiased.
14187 */
14188 void QCPGrid::setAntialiasedZeroLine(bool enabled)
14189 {
14190 mAntialiasedZeroLine = enabled;
14191 }
14192
14193 /*!
14194 Sets the pen with which (major) grid lines are drawn.
14195 */
14196 void QCPGrid::setPen(const QPen &pen)
14197 {
14198 mPen = pen;
14199 }
14200
14201 /*!
14202 Sets the pen with which sub grid lines are drawn.
14203 */
14204 void QCPGrid::setSubGridPen(const QPen &pen)
14205 {
14206 mSubGridPen = pen;
14207 }
14208
14209 /*!
14210 Sets the pen with which zero lines are drawn.
14211
14212 Zero lines are lines at coordinate 0 which may be drawn with a different pen than other grid
14213 lines. To disable zero lines and just draw normal grid lines at zero, set \a pen to Qt::NoPen.
14214 */
14215 void QCPGrid::setZeroLinePen(const QPen &pen)
14216 {
14217 mZeroLinePen = pen;
14218 }
14219
14220 /*! \internal
14221
14222 A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
14223 before drawing the major grid lines.
14224
14225 This is the antialiasing state the painter passed to the \ref draw method is in by default.
14226
14227 This function takes into account the local setting of the antialiasing flag as well as
14228 the overrides set e.g. with \ref QCustomPlot::setNotAntialiasedElements.
14229
14230 \see setAntialiased
14231 */
14232 void QCPGrid::applyDefaultAntialiasingHint(QCPPainter *painter) const
14233 {
14234 applyAntialiasingHint(painter, mAntialiased, QCP::aeGrid);
14235 }
14236
14237 /*! \internal
14238
14239 Draws grid lines and sub grid lines at the positions of (sub) ticks of the parent axis, spanning
14240 over the complete axis rect. Also draws the zero line, if appropriate (\ref setZeroLinePen).
14241
14242 Called by QCustomPlot::draw to draw the grid of an axis.
14243 */
14244 void QCPGrid::draw(QCPPainter *painter)
14245 {
14246 if (!mParentAxis->visible()) return; // also don't draw grid when parent axis isn't visible
14247
14248 if (mSubGridVisible)
14249 drawSubGridLines(painter);
14250 drawGridLines(painter);
14251 }
14252
14253 /*! \internal
14254
14255 Draws the main grid lines and possibly a zero line with the specified painter.
14256
14257 This is a helper function called by \ref draw.
14258 */
14259 void QCPGrid::drawGridLines(QCPPainter *painter) const
14260 {
14261 int lowTick = mParentAxis->mLowestVisibleTick;
14262 int highTick = mParentAxis->mHighestVisibleTick;
14263 double t; // helper variable, result of coordinate-to-pixel transforms
14264 if (mParentAxis->orientation() == Qt::Horizontal)
14265 {
14266 // draw zeroline:
14267 int zeroLineIndex = -1;
14268 if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
14269 {
14270 applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
14271 painter->setPen(mZeroLinePen);
14272 double epsilon = mParentAxis->range().size()*1E-6; // for comparing double to zero
14273 for (int i=lowTick; i <= highTick; ++i)
14274 {
14275 if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
14276 {
14277 zeroLineIndex = i;
14278 t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
14279 painter->drawLine(QLineF(t, mParentAxis->mAxisRect.bottom(), t, mParentAxis->mAxisRect.top()));
14280 break;
14281 }
14282 }
14283 }
14284 applyDefaultAntialiasingHint(painter);
14285 painter->setPen(mPen);
14286 for (int i=lowTick; i <= highTick; ++i)
14287 {
14288 if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
14289 t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
14290 painter->drawLine(QLineF(t, mParentAxis->mAxisRect.bottom(), t, mParentAxis->mAxisRect.top()));
14291 }
14292 } else
14293 {
14294 // draw zeroline:
14295 int zeroLineIndex = -1;
14296 if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
14297 {
14298 applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
14299 painter->setPen(mZeroLinePen);
14300 double epsilon = mParentAxis->mRange.size()*1E-6; // for comparing double to zero
14301 for (int i=lowTick; i <= highTick; ++i)
14302 {
14303 if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
14304 {
14305 zeroLineIndex = i;
14306 t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
14307 painter->drawLine(QLineF(mParentAxis->mAxisRect.left(), t, mParentAxis->mAxisRect.right(), t));
14308 break;
14309 }
14310 }
14311 }
14312 // draw grid lines:
14313 applyDefaultAntialiasingHint(painter);
14314 painter->setPen(mPen);
14315 for (int i=lowTick; i <= highTick; ++i)
14316 {
14317 if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
14318 t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
14319 painter->drawLine(QLineF(mParentAxis->mAxisRect.left(), t, mParentAxis->mAxisRect.right(), t));
14320 }
14321 }
14322 }
14323
14324 /*! \internal
14325
14326 Draws the sub grid lines with the specified painter.
14327
14328 This is a helper function called by \ref draw.
14329 */
14330 void QCPGrid::drawSubGridLines(QCPPainter *painter) const
14331 {
14332 applyAntialiasingHint(painter, mAntialiasedSubGrid, QCP::aeSubGrid);
14333 double t; // helper variable, result of coordinate-to-pixel transforms
14334 painter->setPen(mSubGridPen);
14335 if (mParentAxis->orientation() == Qt::Horizontal)
14336 {
14337 for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
14338 {
14339 t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i)); // x
14340 painter->drawLine(QLineF(t, mParentAxis->mAxisRect.bottom(), t, mParentAxis->mAxisRect.top()));
14341 }
14342 } else
14343 {
14344 for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
14345 {
14346 t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i)); // y
14347 painter->drawLine(QLineF(mParentAxis->mAxisRect.left(), t, mParentAxis->mAxisRect.right(), t));
14348 }
14349 }
14350 }
14351
14352
14353 // ================================================================================
14354 // =================== QCPItemAnchor
14355 // ================================================================================
14356
14357 /*! \class QCPItemAnchor
14358 \brief An anchor of an item to which positions can be attached to.
14359
14360 An item (QCPAbstractItem) may have one or more anchors. Unlike QCPItemPosition, an anchor doesn't
14361 control anything on its item, but provides a way to tie other items via their positions to the
14362 anchor.
14363
14364 For example, a QCPItemRect is defined by its positions \a topLeft and \a bottomRight.
14365 Additionally it has various anchors like \a top, \a topRight or \a bottomLeft etc. So you can
14366 attach the \a start (which is a QCPItemPosition) of a QCPItemLine to one of the anchors by
14367 calling QCPItemPosition::setParentAnchor on \a start, passing the wanted anchor of the
14368 QCPItemRect. This way the start of the line will now always follow the respective anchor location
14369 on the rect item.
14370
14371 Note that QCPItemPosition derives from QCPItemAnchor, so every position can also serve as an
14372 anchor to other positions.
14373
14374 To learn how to provide anchors in your own item subclasses, see the subclassing section of the
14375 QCPAbstractItem documentation.
14376 */
14377
14378 /*!
14379 Creates a new QCPItemAnchor. You shouldn't create QCPItemAnchor instances directly, even if
14380 you want to make a new item subclass. Use \ref QCPAbstractItem::createAnchor instead, as
14381 explained in the subclassing section of the QCPAbstractItem documentation.
14382 */
14383 QCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name, int anchorId) :
14384 mParentPlot(parentPlot),
14385 mParentItem(parentItem),
14386 mAnchorId(anchorId),
14387 mName(name)
14388 {
14389 }
14390
14391 QCPItemAnchor::~QCPItemAnchor()
14392 {
14393 // unregister as parent at children:
14394 QList<QCPItemPosition*> currentChildren(mChildren.toList());
14395 for (int i=0; i<currentChildren.size(); ++i)
14396 currentChildren.at(i)->setParentAnchor(0); // this acts back on this anchor and child removes itself from mChildren
14397 }
14398
14399 /*!
14400 Returns the final absolute pixel position of the QCPItemAnchor on the QCustomPlot surface.
14401
14402 The pixel information is internally retrieved via QCPAbstractItem::anchorPixelPosition of the
14403 parent item, QCPItemAnchor is just an intermediary.
14404 */
14405 QPointF QCPItemAnchor::pixelPoint() const
14406 {
14407 if (mParentItem)
14408 {
14409 if (mAnchorId > -1)
14410 {
14411 return mParentItem->anchorPixelPoint(mAnchorId);
14412 } else
14413 {
14414 qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId;
14415 return QPointF();
14416 }
14417 } else
14418 {
14419 qDebug() << Q_FUNC_INFO << "no parent item set";
14420 return QPointF();
14421 }
14422 }
14423
14424 /*! \internal
14425
14426 Adds \a pos to the child list of this anchor. This is necessary to notify the children prior to
14427 destruction of the anchor.
14428
14429 Note that this function does not change the parent setting in \a pos.
14430 */
14431 void QCPItemAnchor::addChild(QCPItemPosition *pos)
14432 {
14433 if (!mChildren.contains(pos))
14434 mChildren.insert(pos);
14435 else
14436 qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
14437 }
14438
14439 /*! \internal
14440
14441 Removes \a pos from the child list of this anchor.
14442
14443 Note that this function does not change the parent setting in \a pos.
14444 */
14445 void QCPItemAnchor::removeChild(QCPItemPosition *pos)
14446 {
14447 if (!mChildren.remove(pos))
14448 qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
14449 }
14450
14451
14452 // ================================================================================
14453 // =================== QCPItemBracket
14454 // ================================================================================
14455
14456 /*! \class QCPItemBracket
14457 \brief A bracket for referencing/highlighting certain parts in the plot.
14458
14459 \image html QCPItemBracket.png "Bracket example. Blue dotted circles are anchors, solid blue discs are positions."
14460
14461 It has two positions, \a left and \a right, which define the span of the bracket. If \a left is
14462 actually farther to the left than \a right, the bracket is opened to the bottom, as shown in the
14463 example image.
14464
14465 The bracket supports multiple styles via \ref setStyle. The length, i.e. how far the bracket
14466 stretches away from the embraced span, can be controlled with \ref setLength.
14467
14468 \image html QCPItemBracket-length.png
14469 <center>Demonstrating the effect of different values for \ref setLength, for styles \ref
14470 bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.</center>
14471
14472 It provides an anchor \a center, to allow connection of other items, e.g. an arrow (QCPItemLine
14473 or QCPItemCurve) or a text label (QCPItemText), to the bracket.
14474 */
14475
14476 /*!
14477 Creates a bracket item and sets default values.
14478
14479 The constructed item can be added to the plot with QCustomPlot::addItem.
14480 */
14481 QCPItemBracket::QCPItemBracket(QCustomPlot *parentPlot) :
14482 QCPAbstractItem(parentPlot),
14483 left(createPosition("left")),
14484 right(createPosition("right")),
14485 center(createAnchor("center", aiCenter))
14486 {
14487 left->setCoords(0, 0);
14488 right->setCoords(1, 1);
14489
14490 setPen(QPen(Qt::black));
14491 setSelectedPen(QPen(Qt::blue, 2));
14492 setLength(8);
14493 setStyle(bsCalligraphic);
14494 }
14495
14496 QCPItemBracket::~QCPItemBracket()
14497 {
14498 }
14499
14500 /*!
14501 Sets the pen that will be used to draw the bracket.
14502
14503 Note that when the style is \ref bsCalligraphic, only the color will be taken from the pen, the
14504 stroke and width are ignored. To change the apparent stroke width of a calligraphic bracket, use
14505 \ref setLength, which has a similar effect.
14506
14507 \see setSelectedPen
14508 */
14509 void QCPItemBracket::setPen(const QPen &pen)
14510 {
14511 mPen = pen;
14512 }
14513
14514 /*!
14515 Sets the pen that will be used to draw the bracket when selected
14516
14517 \see setPen, setSelected
14518 */
14519 void QCPItemBracket::setSelectedPen(const QPen &pen)
14520 {
14521 mSelectedPen = pen;
14522 }
14523
14524 /*!
14525 Sets the \a length in pixels how far the bracket extends in the direction towards the embraced
14526 span of the bracket (i.e. perpendicular to the <i>left</i>-<i>right</i>-direction)
14527
14528 \image html QCPItemBracket-length.png
14529 <center>Demonstrating the effect of different values for \ref setLength, for styles \ref
14530 bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.</center>
14531 */
14532 void QCPItemBracket::setLength(double length)
14533 {
14534 mLength = length;
14535 }
14536
14537 /*!
14538 Sets the style of the bracket, i.e. the shape/visual appearance.
14539
14540 \see setPen
14541 */
14542 void QCPItemBracket::setStyle(QCPItemBracket::BracketStyle style)
14543 {
14544 mStyle = style;
14545 }
14546
14547 /* inherits documentation from base class */
14548 double QCPItemBracket::selectTest(const QPointF &pos) const
14549 {
14550 if (!mVisible)
14551 return -1;
14552
14553 QVector2D leftVec(left->pixelPoint());
14554 QVector2D rightVec(right->pixelPoint());
14555 if (leftVec.toPoint() == rightVec.toPoint())
14556 return -1;
14557
14558 QVector2D widthVec = (rightVec-leftVec)*0.5;
14559 QVector2D lengthVec(-widthVec.y(), widthVec.x());
14560 lengthVec = lengthVec.normalized()*mLength;
14561 QVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
14562
14563 return qSqrt(distSqrToLine((centerVec-widthVec).toPointF(), (centerVec+widthVec).toPointF(), pos));
14564 }
14565
14566 /* inherits documentation from base class */
14567 void QCPItemBracket::draw(QCPPainter *painter)
14568 {
14569 QVector2D leftVec(left->pixelPoint());
14570 QVector2D rightVec(right->pixelPoint());
14571 if (leftVec.toPoint() == rightVec.toPoint())
14572 return;
14573
14574 QVector2D widthVec = (rightVec-leftVec)*0.5;
14575 QVector2D lengthVec(-widthVec.y(), widthVec.x());
14576 lengthVec = lengthVec.normalized()*mLength;
14577 QVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
14578
14579 QPolygon boundingPoly;
14580 boundingPoly << leftVec.toPoint() << rightVec.toPoint()
14581 << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint();
14582 QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
14583 if (clip.intersects(boundingPoly.boundingRect()))
14584 {
14585 painter->setPen(mainPen());
14586 switch (mStyle)
14587 {
14588 case bsSquare:
14589 {
14590 painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF());
14591 painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
14592 painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
14593 break;
14594 }
14595 case bsRound:
14596 {
14597 painter->setBrush(Qt::NoBrush);
14598 QPainterPath path;
14599 path.moveTo((centerVec+widthVec+lengthVec).toPointF());
14600 path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF());
14601 path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
14602 painter->drawPath(path);
14603 break;
14604 }
14605 case bsCurly:
14606 {
14607 painter->setBrush(Qt::NoBrush);
14608 QPainterPath path;
14609 path.moveTo((centerVec+widthVec+lengthVec).toPointF());
14610 path.cubicTo((centerVec+widthVec*1-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+1*lengthVec).toPointF(), centerVec.toPointF());
14611 path.cubicTo((centerVec-0.4*widthVec+1*lengthVec).toPointF(), (centerVec-widthVec*1-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
14612 painter->drawPath(path);
14613 break;
14614 }
14615 case bsCalligraphic:
14616 {
14617 painter->setPen(Qt::NoPen);
14618 painter->setBrush(QBrush(mainPen().color()));
14619 QPainterPath path;
14620 path.moveTo((centerVec+widthVec+lengthVec).toPointF());
14621
14622 path.cubicTo((centerVec+widthVec*1-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+0.8*lengthVec).toPointF(), centerVec.toPointF());
14623 path.cubicTo((centerVec-0.4*widthVec+0.8*lengthVec).toPointF(), (centerVec-widthVec*1-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
14624
14625 path.cubicTo((centerVec-widthVec*1-lengthVec*0.5).toPointF(), (centerVec-0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+lengthVec*0.2).toPointF());
14626 path.cubicTo((centerVec+0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+widthVec*1-lengthVec*0.5).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
14627
14628 painter->drawPath(path);
14629 break;
14630 }
14631 }
14632 }
14633 }
14634
14635 /* inherits documentation from base class */
14636 QPointF QCPItemBracket::anchorPixelPoint(int anchorId) const
14637 {
14638 QVector2D leftVec(left->pixelPoint());
14639 QVector2D rightVec(right->pixelPoint());
14640 if (leftVec.toPoint() == rightVec.toPoint())
14641 return leftVec.toPointF();
14642
14643 QVector2D widthVec = (rightVec-leftVec)*0.5;
14644 QVector2D lengthVec(-widthVec.y(), widthVec.x());
14645 lengthVec = lengthVec.normalized()*mLength;
14646 QVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
14647
14648 switch (anchorId)
14649 {
14650 case aiCenter:
14651 return centerVec.toPointF();
14652 }
14653 qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
14654 return QPointF();
14655 }
14656
14657 /*! \internal
14658
14659 Returns the pen that should be used for drawing lines. Returns mPen when the
14660 item is not selected and mSelectedPen when it is.
14661 */
14662 QPen QCPItemBracket::mainPen() const
14663 {
14664 return mSelected ? mSelectedPen : mPen;
14665 }
14666
14667
14668 // ================================================================================
14669 // =================== QCPItemTracer
14670 // ================================================================================
14671
14672 /*! \class QCPItemTracer
14673 \brief Item that sticks to QCPGraph data points
14674
14675 \image html QCPItemTracer.png "Tracer example. Blue dotted circles are anchors, solid blue discs are positions."
14676
14677 The tracer can be connected with a QCPGraph via \ref setGraph. Then it will automatically adopt
14678 the coordinate axes of the graph and update its \a position to be on the graph's data. This means
14679 the key stays controllable via \ref setGraphKey, but the value will follow the graph data. If a
14680 QCPGraph is connected, note that setting the coordinates directly via \a position will have no
14681 effect, i.e. be overriden in the next redraw (this is when the coodinate update happens).
14682
14683 If the specified key in \ref setGraphKey is outside the key bounds of the graph, the tracer will
14684 stay at the respective end of the graph.
14685
14686 With \ref setInterpolating you may specify whether the tracer may only stay exactly on data
14687 points or whether it interpolates data points linearly, if given a key that lies between two data
14688 points of the graph.
14689
14690 The tracer has different visual styles, see \ref setStyle. It is also possible to make the tracer
14691 have no own visual appearance (set the style to \ref tsNone), and just connect other item
14692 positions to the tracer \a position (used as an anchor) via \ref
14693 QCPItemPosition::setParentAnchor.
14694
14695 \note The tracer position is only automatically updated upon redraws. This means when, for
14696 example, the data of the graph changes and you immediately afterwards (without a redraw) read the
14697 \a position coordinates of the tracer, they will not reflect the updated data of the graph. In
14698 this case you should call \ref updatePosition manually, prior to reading the tracer coordinates.
14699 */
14700
14701 /*!
14702 Creates a tracer item and sets default values.
14703
14704 The constructed item can be added to the plot with QCustomPlot::addItem.
14705 */
14706 QCPItemTracer::QCPItemTracer(QCustomPlot *parentPlot) :
14707 QCPAbstractItem(parentPlot),
14708 position(createPosition("position")),
14709 mGraph(0)
14710 {
14711 position->setCoords(0, 0);
14712
14713 setBrush(Qt::NoBrush);
14714 setSelectedBrush(Qt::NoBrush);
14715 setPen(QPen(Qt::black));
14716 setSelectedPen(QPen(Qt::blue, 2));
14717 setStyle(tsCrosshair);
14718 setSize(6);
14719 setInterpolating(false);
14720 setGraphKey(0);
14721 }
14722
14723 QCPItemTracer::~QCPItemTracer()
14724 {
14725 }
14726
14727 /*!
14728 Sets the pen that will be used to draw the line of the tracer
14729
14730 \see setSelectedPen, setBrush
14731 */
14732 void QCPItemTracer::setPen(const QPen &pen)
14733 {
14734 mPen = pen;
14735 }
14736
14737 /*!
14738 Sets the pen that will be used to draw the line of the tracer when selected
14739
14740 \see setPen, setSelected
14741 */
14742 void QCPItemTracer::setSelectedPen(const QPen &pen)
14743 {
14744 mSelectedPen = pen;
14745 }
14746
14747 /*!
14748 Sets the brush that will be used to draw any fills of the tracer
14749
14750 \see setSelectedBrush, setPen
14751 */
14752 void QCPItemTracer::setBrush(const QBrush &brush)
14753 {
14754 mBrush = brush;
14755 }
14756
14757 /*!
14758 Sets the brush that will be used to draw any fills of the tracer, when selected.
14759
14760 \see setBrush, setSelected
14761 */
14762 void QCPItemTracer::setSelectedBrush(const QBrush &brush)
14763 {
14764 mSelectedBrush = brush;
14765 }
14766
14767 /*!
14768 Sets the size of the tracer in pixels, if the style supports setting a size (e.g. \ref tsSquare
14769 does, \ref tsCrosshair does not).
14770 */
14771 void QCPItemTracer::setSize(double size)
14772 {
14773 mSize = size;
14774 }
14775
14776 /*!
14777 Sets the style/visual appearance of the tracer.
14778
14779 If you only want to use the tracer \a position as an anchor for other items, set \a style to
14780 \ref tsNone.
14781 */
14782 void QCPItemTracer::setStyle(QCPItemTracer::TracerStyle style)
14783 {
14784 mStyle = style;
14785 }
14786
14787 /*!
14788 Sets the QCPGraph this tracer sticks to. The tracer \a position will be set to type
14789 QCPItemPosition::ptPlotCoords and the axes will be set to the axes of \a graph.
14790
14791 To free the tracer from any graph, set \a graph to 0. The tracer \a position can then be placed
14792 freely like any other item position. This is the state the tracer will assume when its graph gets
14793 deleted while still attached to it.
14794
14795 \see setGraphKey
14796 */
14797 void QCPItemTracer::setGraph(QCPGraph *graph)
14798 {
14799 if (graph)
14800 {
14801 if (graph->parentPlot() == mParentPlot)
14802 {
14803 position->setType(QCPItemPosition::ptPlotCoords);
14804 position->setAxes(graph->keyAxis(), graph->valueAxis());
14805 mGraph = graph;
14806 updatePosition();
14807 } else
14808 qDebug() << Q_FUNC_INFO << "graph isn't in same QCustomPlot instance as this item";
14809 } else
14810 {
14811 mGraph = 0;
14812 }
14813 }
14814
14815 /*!
14816 Sets the key of the graph's data point the tracer will be positioned at. This is the only free
14817 cordinate of a tracer when attached to a graph.
14818
14819 Depending on \ref setInterpolating, the tracer will be either positioned on the data point
14820 closest to \a key, or will stay exactly at \a key and interpolate the value linearly.
14821
14822 \see setGraph, setInterpolating
14823 */
14824 void QCPItemTracer::setGraphKey(double key)
14825 {
14826 mGraphKey = key;
14827 }
14828
14829 /*!
14830 Sets whether the value of the graph's data points shall be interpolated, when positioning the
14831 tracer.
14832
14833 If \a enabled is set to false and a key is given with \ref setGraphKey, the tracer is placed on
14834 the data point of the graph which is closest to the key, but which is not necessarily exactly
14835 there. If \a enabled is true, the tracer will be positioned exactly at the specified key, and
14836 the appropriate value will be interpolated from the graph's data points linearly.
14837
14838 \see setGraph, setGraphKey
14839 */
14840 void QCPItemTracer::setInterpolating(bool enabled)
14841 {
14842 mInterpolating = enabled;
14843 }
14844
14845 /* inherits documentation from base class */
14846 double QCPItemTracer::selectTest(const QPointF &pos) const
14847 {
14848 if (!mVisible || mStyle == tsNone)
14849 return -1;
14850
14851 QPointF center(position->pixelPoint());
14852 double w = mSize/2.0;
14853 QRect clip = clipRect();
14854 switch (mStyle)
14855 {
14856 case tsNone: return -1;
14857 case tsPlus:
14858 {
14859 if (clipRect().intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
14860 return qSqrt(qMin(distSqrToLine(center+QPointF(-w, 0), center+QPointF(w, 0), pos),
14861 distSqrToLine(center+QPointF(0, -w), center+QPointF(0, w), pos)));
14862 break;
14863 }
14864 case tsCrosshair:
14865 {
14866 return qSqrt(qMin(distSqrToLine(QPointF(clip.left(), center.y()), QPointF(clip.right(), center.y()), pos),
14867 distSqrToLine(QPointF(center.x(), clip.top()), QPointF(center.x(), clip.bottom()), pos)));
14868 break;
14869 }
14870 case tsCircle:
14871 {
14872 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
14873 {
14874 // distance to border:
14875 double centerDist = QVector2D(center-pos).length();
14876 double circleLine = w;
14877 double result = qAbs(centerDist-circleLine);
14878 // filled ellipse, allow click inside to count as hit:
14879 if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
14880 {
14881 if (centerDist <= circleLine)
14882 result = mParentPlot->selectionTolerance()*0.99;
14883 }
14884 return result;
14885 }
14886 break;
14887 }
14888 case tsSquare:
14889 {
14890 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
14891 {
14892 QRectF rect = QRectF(center-QPointF(w, w), center+QPointF(w, w));
14893 bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
14894 return rectSelectTest(rect, pos, filledRect);
14895 }
14896 break;
14897 }
14898 }
14899 return -1;
14900 }
14901
14902 /* inherits documentation from base class */
14903 void QCPItemTracer::draw(QCPPainter *painter)
14904 {
14905 updatePosition();
14906 if (mStyle == tsNone)
14907 return;
14908
14909 painter->setPen(mainPen());
14910 painter->setBrush(mainBrush());
14911 QPointF center(position->pixelPoint());
14912 double w = mSize/2.0;
14913 QRect clip = clipRect();
14914 switch (mStyle)
14915 {
14916 case tsNone: return;
14917 case tsPlus:
14918 {
14919 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
14920 {
14921 painter->drawLine(QLineF(center+QPointF(-w, 0), center+QPointF(w, 0)));
14922 painter->drawLine(QLineF(center+QPointF(0, -w), center+QPointF(0, w)));
14923 }
14924 break;
14925 }
14926 case tsCrosshair:
14927 {
14928 if (center.y() > clip.top() && center.y() < clip.bottom())
14929 painter->drawLine(QLineF(clip.left(), center.y(), clip.right(), center.y()));
14930 if (center.x() > clip.left() && center.x() < clip.right())
14931 painter->drawLine(QLineF(center.x(), clip.top(), center.x(), clip.bottom()));
14932 break;
14933 }
14934 case tsCircle:
14935 {
14936 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
14937 painter->drawEllipse(center, w, w);
14938 break;
14939 }
14940 case tsSquare:
14941 {
14942 if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
14943 painter->drawRect(QRectF(center-QPointF(w, w), center+QPointF(w, w)));
14944 break;
14945 }
14946 }
14947 }
14948
14949 /*!
14950 If the tracer is connected with a graph (\ref setGraph), this function updates the tracer's \a
14951 position to reside on the graph data, depending on the configured key (\ref setGraphKey).
14952
14953 It is called automatically on every redraw and normally doesn't need to be called manually. One
14954 exception is when you want to read the tracer coordinates via \a position and are not sure that
14955 the graph's data (or the tracer key with \ref setGraphKey) hasn't changed since the last redraw.
14956 In that situation, call this function before accessing \a position, to make sure you don't get
14957 out-of-date coordinates.
14958
14959 If there is no graph set on this tracer, this function does nothing.
14960 */
14961 void QCPItemTracer::updatePosition()
14962 {
14963 if (mGraph)
14964 {
14965 if (mParentPlot->hasPlottable(mGraph))
14966 {
14967 if (mGraph->data()->size() > 1)
14968 {
14969 QCPDataMap::const_iterator first = mGraph->data()->constBegin();
14970 QCPDataMap::const_iterator last = mGraph->data()->constEnd()-1;
14971 if (mGraphKey < first.key())
14972 position->setCoords(first.key(), first.value().value);
14973 else if (mGraphKey > last.key())
14974 position->setCoords(last.key(), last.value().value);
14975 else
14976 {
14977 QCPDataMap::const_iterator it = first;
14978 it = mGraph->data()->lowerBound(mGraphKey);
14979 if (it != first) // mGraphKey is somewhere between iterators
14980 {
14981 QCPDataMap::const_iterator prevIt = it-1;
14982 if (mInterpolating)
14983 {
14984 // interpolate between iterators around mGraphKey:
14985 double slope = (it.value().value-prevIt.value().value)/(it.key()-prevIt.key());
14986 position->setCoords(mGraphKey, (mGraphKey-prevIt.key())*slope+prevIt.value().value);
14987 } else
14988 {
14989 // find iterator with key closest to mGraphKey:
14990 if (mGraphKey < (prevIt.key()+it.key())*0.5)
14991 it = prevIt;
14992 position->setCoords(it.key(), it.value().value);
14993 }
14994 } else // mGraphKey is exactly on first iterator
14995 position->setCoords(it.key(), it.value().value);
14996 }
14997 } else if (mGraph->data()->size() == 1)
14998 {
14999 QCPDataMap::const_iterator it = mGraph->data()->constBegin();
15000 position->setCoords(it.key(), it.value().value);
15001 } else
15002 qDebug() << Q_FUNC_INFO << "graph has no data";
15003 } else
15004 qDebug() << Q_FUNC_INFO << "graph not contained in QCustomPlot instance (anymore)";
15005 }
15006 }
15007
15008 /*! \internal
15009
15010 Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
15011 and mSelectedPen when it is.
15012 */
15013 QPen QCPItemTracer::mainPen() const
15014 {
15015 return mSelected ? mSelectedPen : mPen;
15016 }
15017
15018 /*! \internal
15019
15020 Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
15021 is not selected and mSelectedBrush when it is.
15022 */
15023 QBrush QCPItemTracer::mainBrush() const
15024 {
15025 return mSelected ? mSelectedBrush : mBrush;
15026 }
15027
15028
15029
15030
15031
15032
15033
This diff has been collapsed as it changes many lines, (2171 lines changed) Show them Hide them
@@ -0,0 +1,2171
1 /***************************************************************************
2 ** **
3 ** QCustomPlot, a simple to use, modern plotting widget for Qt **
4 ** Copyright (C) 2012 Emanuel Eichhammer **
5 ** **
6 ** This program is free software: you can redistribute it and/or modify **
7 ** it under the terms of the GNU General Public License as published by **
8 ** the Free Software Foundation, either version 3 of the License, or **
9 ** (at your option) any later version. **
10 ** **
11 ** This program is distributed in the hope that it will be useful, **
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of **
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the **
14 ** GNU General Public License for more details. **
15 ** **
16 ** You should have received a copy of the GNU General Public License **
17 ** along with this program. If not, see http://www.gnu.org/licenses/. **
18 ** **
19 ****************************************************************************
20 ** Author: Emanuel Eichhammer **
21 ** Website/Contact: http://www.WorksLikeClockwork.com/ **
22 ** Date: 09.06.12 **
23 ****************************************************************************/
24
25 /*! \file */
26
27 #ifndef QCUSTOMPLOT_H
28 #define QCUSTOMPLOT_H
29
30 #include <QObject>
31 #include <QWidget>
32 #include <QPainter>
33 #include <QPaintEvent>
34 #include <QPixmap>
35 #include <QVector>
36 #include <QString>
37 #include <QPrinter>
38 #include <QDateTime>
39 #include <QMultiMap>
40 #include <QFlags>
41 #include <QDebug>
42 #include <QVector2D>
43 #include <QStack>
44 #include <qmath.h>
45 #include <limits>
46
47 // decl definitions for shared library compilation/usage:
48 #if defined(QCUSTOMPLOT_COMPILE_LIBRARY)
49 # define QCP_LIB_DECL Q_DECL_EXPORT
50 #elif defined(QCUSTOMPLOT_USE_LIBRARY)
51 # define QCP_LIB_DECL Q_DECL_IMPORT
52 #else
53 # define QCP_LIB_DECL
54 #endif
55
56 class QCustomPlot;
57 class QCPLegend;
58 class QCPRange;
59 class QCPLayerable;
60 class QCPAbstractItem;
61 class QCPItemPosition;
62 class QCPAxis;
63 class QCPData;
64
65 /*!
66 The QCP Namespace contains general enums and QFlags
67 */
68 namespace QCP
69 {
70 /*!
71 Defines the symbol used for scatter points.
72
73 On plottables/items that draw scatters, the sizes of these visualizations (with exception of \ref
74 QCP::ssDot and \ref QCP::ssPixmap) can be controlled with a \a setScatterSize function. Scatters
75 are in general drawn with the main pen set on the plottable/item.
76
77 \see QCPGraph::setScatterStyle, QCPStatisticalBox::setOutlierStyle
78 */
79 enum ScatterStyle { ssNone ///< no scatter symbols are drawn (e.g. in QCPGraph, data only represented with lines)
80 ,ssDot ///< a single pixel
81 ,ssCross ///< a cross (x)
82 ,ssPlus ///< a plus (+)
83 ,ssCircle ///< a circle which is not filled
84 ,ssDisc ///< a circle which is filled with the color of the pen (not the brush!)
85 ,ssSquare ///< a square which is not filled
86 ,ssDiamond ///< a diamond which is not filled
87 ,ssStar ///< a star with eight arms, i.e. a combination of cross and plus
88 ,ssTriangle ///< an equilateral triangle which is not filled, standing on baseline
89 ,ssTriangleInverted ///< an equilateral triangle which is not filled, standing on corner
90 ,ssCrossSquare ///< a square which is not filled, with a cross inside
91 ,ssPlusSquare ///< a square which is not filled, with a plus inside
92 ,ssCrossCircle ///< a circle which is not filled, with a cross inside
93 ,ssPlusCircle ///< a circle which is not filled, with a plus inside
94 ,ssPeace ///< a circle which is not filled, with one vertical and two downward diagonal lines
95 ,ssPixmap ///< a custom pixmap specified by setScatterPixmap, centered on the data point coordinates
96 };
97
98 /*!
99 Defines what elements of a plot can be forcibly drawn antialiased/not antialiased. If an
100 element is neither forcibly drawn antialiased nor forcibly drawn not antialiased, it is up to
101 the respective element how it is drawn. Typically it provides a \a setAntialiased function for
102 this.
103
104 \c AntialiasedElements is a flag of or-combined elements of this enum type.
105
106 \see QCustomPlot::setAntialiasedElements, QCustomPlot::setNotAntialiasedElements
107 */
108 enum AntialiasedElement { aeAxes = 0x0001 ///< <tt>0x0001</tt> Axis base line and tick marks
109 ,aeGrid = 0x0002 ///< <tt>0x0002</tt> Grid lines
110 ,aeSubGrid = 0x0004 ///< <tt>0x0004</tt> Sub grid lines
111 ,aeLegend = 0x0008 ///< <tt>0x0008</tt> Legend box
112 ,aeLegendItems = 0x0010 ///< <tt>0x0010</tt> Legend items
113 ,aePlottables = 0x0020 ///< <tt>0x0020</tt> Main lines of plottables (excluding error bars, see element \ref aeErrorBars)
114 ,aeItems = 0x0040 ///< <tt>0x0040</tt> Main lines of items
115 ,aeScatters = 0x0080 ///< <tt>0x0080</tt> Scatter symbols of plottables (excluding scatter symbols of type ssPixmap)
116 ,aeErrorBars = 0x0100 ///< <tt>0x0100</tt> Error bars
117 ,aeFills = 0x0200 ///< <tt>0x0200</tt> Borders of fills (e.g. under or between graphs)
118 ,aeZeroLine = 0x0400 ///< <tt>0x0400</tt> Zero-lines, see \ref QCPAxis::setZeroLinePen
119 ,aeAll = 0xFFFF ///< <tt>0xFFFF</tt> All elements
120 ,aeNone = 0x0000 ///< <tt>0x0000</tt> No elements
121 };
122 Q_DECLARE_FLAGS(AntialiasedElements, AntialiasedElement)
123
124 /*!
125 Defines plotting hints that control various aspects of the quality and speed of plotting.
126 \see QCustomPlot::setPlottingHints
127 */
128 enum PlottingHint { phNone = 0x000 ///< <tt>0x000</tt> No hints are set
129 ,phFastPolylines = 0x001 ///< <tt>0x001</tt> Graph/Curve lines are drawn with a faster method. This reduces the quality
130 ///< especially of the line segment joins. (Only used for solid line pens.)
131 ,phForceRepaint = 0x002 ///< <tt>0x002</tt> causes an immediate repaint() instead of a soft update() when QCustomPlot::replot() is called. This is set by default
132 ///< on Windows-Systems to prevent the plot from freezing on fast consecutive replots (e.g. user drags ranges with mouse).
133 };
134 Q_DECLARE_FLAGS(PlottingHints, PlottingHint)
135 }
136
137 Q_DECLARE_OPERATORS_FOR_FLAGS(QCP::AntialiasedElements)
138 Q_DECLARE_OPERATORS_FOR_FLAGS(QCP::PlottingHints)
139
140 class QCP_LIB_DECL QCPData
141 {
142 public:
143 QCPData();
144 QCPData(double key, double value);
145 double key, value;
146 double keyErrorPlus, keyErrorMinus;
147 double valueErrorPlus, valueErrorMinus;
148 };
149 Q_DECLARE_TYPEINFO(QCPData, Q_MOVABLE_TYPE);
150
151 /*! \typedef QCPDataMap
152 Container for storing QCPData items in a sorted fashion. The key of the map
153 is the key member of the QCPData instance.
154
155 This is the container in which QCPGraph holds its data.
156 \see QCPData, QCPGraph::setData
157 */
158 typedef QMap<double, QCPData> QCPDataMap;
159 typedef QMapIterator<double, QCPData> QCPDataMapIterator;
160 typedef QMutableMapIterator<double, QCPData> QCPDataMutableMapIterator;
161
162 class QCP_LIB_DECL QCPCurveData
163 {
164 public:
165 QCPCurveData();
166 QCPCurveData(double t, double key, double value);
167 double t, key, value;
168 };
169 Q_DECLARE_TYPEINFO(QCPCurveData, Q_MOVABLE_TYPE);
170
171 /*! \typedef QCPCurveDataMap
172 Container for storing QCPCurveData items in a sorted fashion. The key of the map
173 is the t member of the QCPCurveData instance.
174
175 This is the container in which QCPCurve holds its data.
176 \see QCPCurveData, QCPCurve::setData
177 */
178
179 typedef QMap<double, QCPCurveData> QCPCurveDataMap;
180 typedef QMapIterator<double, QCPCurveData> QCPCurveDataMapIterator;
181 typedef QMutableMapIterator<double, QCPCurveData> QCPCurveDataMutableMapIterator;
182
183 class QCP_LIB_DECL QCPBarData
184 {
185 public:
186 QCPBarData();
187 QCPBarData(double key, double value);
188 double key, value;
189 };
190 Q_DECLARE_TYPEINFO(QCPBarData, Q_MOVABLE_TYPE);
191
192 /*! \typedef QCPBarDataMap
193 Container for storing QCPBarData items in a sorted fashion. The key of the map
194 is the key member of the QCPBarData instance.
195
196 This is the container in which QCPBars holds its data.
197 \see QCPBarData, QCPBars::setData
198 */
199 typedef QMap<double, QCPBarData> QCPBarDataMap;
200 typedef QMapIterator<double, QCPBarData> QCPBarDataMapIterator;
201 typedef QMutableMapIterator<double, QCPBarData> QCPBarDataMutableMapIterator;
202
203 class QCP_LIB_DECL QCPPainter : public QPainter
204 {
205 public:
206 QCPPainter();
207 QCPPainter(QPaintDevice *device);
208 ~QCPPainter();
209
210 // getters:
211 QPixmap scatterPixmap() const { return mScatterPixmap; }
212 bool antialiasing() const { return testRenderHint(QPainter::Antialiasing); }
213 bool pdfExportMode() const { return mPdfExportMode; }
214 bool scaledExportMode() const { return mScaledExportMode; }
215
216 // setters:
217 void setScatterPixmap(const QPixmap pm);
218 void setAntialiasing(bool enabled);
219 void setPdfExportMode(bool enabled);
220 void setScaledExportMode(bool enabled);
221
222 // methods hiding non-virtual base class functions (QPainter bug workarounds):
223 void setPen(const QPen &pen);
224 void setPen(const QColor &color);
225 void setPen(Qt::PenStyle penStyle);
226 void drawLine(const QLineF &line);
227 void drawLine(const QPointF &p1, const QPointF &p2) {drawLine(QLineF(p1, p2));}
228 void save();
229 void restore();
230
231 // helpers:
232 void fixScaledPen();
233 void drawScatter(double x, double y, double size, QCP::ScatterStyle style);
234
235 protected:
236 QPixmap mScatterPixmap;
237 bool mScaledExportMode;
238 bool mPdfExportMode;
239 bool mIsAntialiasing;
240 QStack<bool> mAntialiasingStack;
241 };
242
243 class QCP_LIB_DECL QCPLineEnding
244 {
245 public:
246 /*!
247 Defines the type of ending decoration for line-like items, e.g. an arrow.
248
249 \image html QCPLineEnding.png
250
251 The width and length of these decorations can be controlled with the functions \ref setWidth
252 and \ref setLength. Some decorations like \ref esDisc, \ref esSquare, \ref esDiamond and \ref esBar only
253 support a width, the length property is ignored.
254
255 \see QCPItemLine::setHead, QCPItemLine::setTail, QCPItemCurve::setHead, QCPItemCurve::setTail
256 */
257 enum EndingStyle { esNone ///< No ending decoration
258 ,esFlatArrow ///< A filled arrow head with a straight/flat back (a triangle)
259 ,esSpikeArrow ///< A filled arrow head with an indented back
260 ,esLineArrow ///< A non-filled arrow head with open back
261 ,esDisc ///< A filled circle
262 ,esSquare ///< A filled square
263 ,esDiamond ///< A filled diamond (45° rotated square)
264 ,esBar ///< A bar perpendicular to the line
265 };
266
267 QCPLineEnding();
268 QCPLineEnding(EndingStyle style, double width=8, double length=10, bool inverted=false);
269
270 // getters:
271 EndingStyle style() const { return mStyle; }
272 double width() const { return mWidth; }
273 double length() const { return mLength; }
274 bool inverted() const { return mInverted; }
275
276 // setters:
277 void setStyle(EndingStyle style);
278 void setWidth(double width);
279 void setLength(double length);
280 void setInverted(bool inverted);
281
282 // non-property methods:
283 double boundingDistance() const;
284 void draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const;
285 void draw(QCPPainter *painter, const QVector2D &pos, double angle) const;
286
287 protected:
288 EndingStyle mStyle;
289 double mWidth, mLength;
290 bool mInverted;
291 };
292 Q_DECLARE_TYPEINFO(QCPLineEnding, Q_MOVABLE_TYPE);
293
294 class QCP_LIB_DECL QCPLayer
295 {
296 public:
297 QCPLayer(QCustomPlot* parentPlot, const QString &layerName);
298 ~QCPLayer();
299
300 // getters:
301 QCustomPlot *parentPlot() const { return mParentPlot; }
302 QString name() const { return mName; }
303 int index() const;
304 QList<QCPLayerable*> children() const { return mChildren; }
305
306 protected:
307 QCustomPlot *mParentPlot;
308 QString mName;
309 QList<QCPLayerable*> mChildren;
310
311 void addChild(QCPLayerable *layerable, bool prepend);
312 void removeChild(QCPLayerable *layerable);
313
314 private:
315 Q_DISABLE_COPY(QCPLayer)
316
317 friend class QCPLayerable;
318 };
319
320 class QCP_LIB_DECL QCPLayerable : public QObject
321 {
322 Q_OBJECT
323 public:
324 QCPLayerable(QCustomPlot *parentPlot);
325 ~QCPLayerable();
326
327 // getters:
328 bool visible() const { return mVisible; }
329 QCustomPlot *parentPlot() const { return mParentPlot; }
330 QCPLayer *layer() const { return mLayer; }
331 bool antialiased() const { return mAntialiased; }
332
333 // setters:
334 void setVisible(bool on);
335 bool setLayer(QCPLayer *layer);
336 bool setLayer(const QString &layerName);
337 void setAntialiased(bool enabled);
338
339 protected:
340 bool mVisible;
341 QCustomPlot *mParentPlot;
342 QCPLayer *mLayer;
343 bool mAntialiased;
344
345 // non-property methods:
346 bool moveToLayer(QCPLayer *layer, bool prepend);
347
348 void applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const;
349 virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const = 0;
350 virtual QRect clipRect() const;
351 virtual void draw(QCPPainter *painter) = 0;
352
353 private:
354 Q_DISABLE_COPY(QCPLayerable)
355
356 friend class QCustomPlot;
357 };
358
359 class QCP_LIB_DECL QCPAbstractPlottable : public QCPLayerable
360 {
361 Q_OBJECT
362 public:
363 QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis);
364 virtual ~QCPAbstractPlottable() {}
365
366 // getters:
367 QString name() const { return mName; }
368 bool antialiasedFill() const { return mAntialiasedFill; }
369 bool antialiasedScatters() const { return mAntialiasedScatters; }
370 bool antialiasedErrorBars() const { return mAntialiasedErrorBars; }
371 QPen pen() const { return mPen; }
372 QPen selectedPen() const { return mSelectedPen; }
373 QBrush brush() const { return mBrush; }
374 QBrush selectedBrush() const { return mSelectedBrush; }
375 QCPAxis *keyAxis() const { return mKeyAxis; }
376 QCPAxis *valueAxis() const { return mValueAxis; }
377 bool selectable() const { return mSelectable; }
378 bool selected() const { return mSelected; }
379
380 // setters:
381 void setName(const QString &name);
382 void setAntialiasedFill(bool enabled);
383 void setAntialiasedScatters(bool enabled);
384 void setAntialiasedErrorBars(bool enabled);
385 void setPen(const QPen &pen);
386 void setSelectedPen(const QPen &pen);
387 void setBrush(const QBrush &brush);
388 void setSelectedBrush(const QBrush &brush);
389 void setKeyAxis(QCPAxis *axis);
390 void setValueAxis(QCPAxis *axis);
391 void setSelectable(bool selectable);
392 void setSelected(bool selected);
393
394 // non-property methods:
395 void rescaleAxes(bool onlyEnlarge=false) const;
396 void rescaleKeyAxis(bool onlyEnlarge=false) const;
397 void rescaleValueAxis(bool onlyEnlarge=false) const;
398 virtual void clearData() = 0;
399 virtual double selectTest(const QPointF &pos) const = 0;
400 virtual bool addToLegend();
401 virtual bool removeFromLegend() const;
402
403 signals:
404 void selectionChanged(bool selected);
405
406 protected:
407 /*!
408 Represents negative and positive sign domain for passing to \ref getKeyRange and \ref getValueRange.
409 */
410 enum SignDomain { sdNegative ///< The negative sign domain, i.e. numbers smaller than zero
411 ,sdBoth ///< Both sign domains, including zero, i.e. all (rational) numbers
412 ,sdPositive ///< The positive sign domain, i.e. numbers greater than zero
413 };
414 QString mName;
415 bool mAntialiasedFill, mAntialiasedScatters, mAntialiasedErrorBars;
416 QPen mPen, mSelectedPen;
417 QBrush mBrush, mSelectedBrush;
418 QCPAxis *mKeyAxis, *mValueAxis;
419 bool mSelected, mSelectable;
420
421 virtual QRect clipRect() const;
422 virtual void draw(QCPPainter *painter) = 0;
423 virtual void drawLegendIcon(QCPPainter *painter, const QRect &rect) const = 0;
424 virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain=sdBoth) const = 0;
425 virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain=sdBoth) const = 0;
426
427 // painting and coordinate transformation helpers:
428 void coordsToPixels(double key, double value, double &x, double &y) const;
429 const QPointF coordsToPixels(double key, double value) const;
430 void pixelsToCoords(double x, double y, double &key, double &value) const;
431 void pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const;
432 QPen mainPen() const;
433 QBrush mainBrush() const;
434 void applyDefaultAntialiasingHint(QCPPainter *painter) const;
435 void applyFillAntialiasingHint(QCPPainter *painter) const;
436 void applyScattersAntialiasingHint(QCPPainter *painter) const;
437 void applyErrorBarsAntialiasingHint(QCPPainter *painter) const;
438
439 // selection test helpers:
440 double distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const;
441
442 private:
443 Q_DISABLE_COPY(QCPAbstractPlottable)
444
445 friend class QCustomPlot;
446 friend class QCPPlottableLegendItem;
447 };
448
449 class QCP_LIB_DECL QCPGraph : public QCPAbstractPlottable
450 {
451 Q_OBJECT
452 public:
453 /*!
454 Defines how the graph's line is represented visually in the plot. The line is drawn with the
455 current pen of the graph (\ref setPen).
456 \see setLineStyle
457 */
458 enum LineStyle { lsNone ///< data points are not connected with any lines (e.g. data only represented
459 ///< with symbols according to the scatter style, see \ref setScatterStyle)
460 ,lsLine ///< data points are connected by a straight line
461 ,lsStepLeft ///< line is drawn as steps where the step height is the value of the left data point
462 ,lsStepRight ///< line is drawn as steps where the step height is the value of the right data point
463 ,lsStepCenter ///< line is drawn as steps where the step is in between two data points
464 ,lsImpulse ///< each data point is represented by a line parallel to the value axis, which reaches from the data point to the zero-value-line
465 };
466 Q_ENUMS(LineStyle)
467 /*!
468 Defines what kind of error bars are drawn for each data point
469 */
470 enum ErrorType { etNone ///< No error bars are shown
471 ,etKey ///< Error bars for the key dimension of the data point are shown
472 ,etValue ///< Error bars for the value dimension of the data point are shown
473 ,etBoth ///< Error bars for both key and value dimensions of the data point are shown
474 };
475 Q_ENUMS(ErrorType)
476
477 explicit QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis);
478 virtual ~QCPGraph();
479
480 // getters:
481 const QCPDataMap *data() const { return mData; }
482 LineStyle lineStyle() const { return mLineStyle; }
483 QCP::ScatterStyle scatterStyle() const { return mScatterStyle; }
484 double scatterSize() const { return mScatterSize; }
485 const QPixmap scatterPixmap() const { return mScatterPixmap; }
486 ErrorType errorType() const { return mErrorType; }
487 QPen errorPen() const { return mErrorPen; }
488 double errorBarSize() const { return mErrorBarSize; }
489 bool errorBarSkipSymbol() const { return mErrorBarSkipSymbol; }
490 QCPGraph *channelFillGraph() const { return mChannelFillGraph; }
491
492 // setters:
493 void setData(QCPDataMap *data, bool copy=false);
494 void setData(const QVector<double> &key, const QVector<double> &value);
495 void setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError);
496 void setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus);
497 void setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueError);
498 void setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus);
499 void setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError, const QVector<double> &valueError);
500 void setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus);
501 void setLineStyle(LineStyle ls);
502 void setScatterStyle(QCP::ScatterStyle ss);
503 void setScatterSize(double size);
504 void setScatterPixmap(const QPixmap &pixmap);
505 void setErrorType(ErrorType errorType);
506 void setErrorPen(const QPen &pen);
507 void setErrorBarSize(double size);
508 void setErrorBarSkipSymbol(bool enabled);
509 void setChannelFillGraph(QCPGraph *targetGraph);
510
511 // non-property methods:
512 void addData(const QCPDataMap &dataMap);
513 void addData(const QCPData &data);
514 void addData(double key, double value);
515 void addData(const QVector<double> &keys, const QVector<double> &values);
516 void removeDataBefore(double key);
517 void removeDataAfter(double key);
518 void removeData(double fromKey, double toKey);
519 void removeData(double key);
520 virtual void clearData();
521 virtual double selectTest(const QPointF &pos) const;
522 using QCPAbstractPlottable::rescaleAxes;
523 using QCPAbstractPlottable::rescaleKeyAxis;
524 using QCPAbstractPlottable::rescaleValueAxis;
525 virtual void rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const; // overloads base class interface
526 virtual void rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const; // overloads base class interface
527 virtual void rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const; // overloads base class interface
528
529 protected:
530 QCPDataMap *mData;
531 QPen mErrorPen;
532 LineStyle mLineStyle;
533 QCP::ScatterStyle mScatterStyle;
534 double mScatterSize;
535 QPixmap mScatterPixmap;
536 ErrorType mErrorType;
537 double mErrorBarSize;
538 bool mErrorBarSkipSymbol;
539 QCPGraph *mChannelFillGraph;
540
541 virtual void draw(QCPPainter *painter);
542 virtual void drawLegendIcon(QCPPainter *painter, const QRect &rect) const;
543
544 // functions to generate plot data points in pixel coordinates:
545 void getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
546 // plot style specific functions to generate plot data, used by getPlotData:
547 void getScatterPlotData(QVector<QCPData> *pointData) const;
548 void getLinePlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
549 void getStepLeftPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
550 void getStepRightPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
551 void getStepCenterPlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
552 void getImpulsePlotData(QVector<QPointF> *lineData, QVector<QCPData> *pointData) const;
553
554 // helper functions for drawing:
555 void drawFill(QCPPainter *painter, QVector<QPointF> *lineData) const;
556 void drawScatterPlot(QCPPainter *painter, QVector<QCPData> *pointData) const;
557 void drawLinePlot(QCPPainter *painter, QVector<QPointF> *lineData) const;
558 void drawImpulsePlot(QCPPainter *painter, QVector<QPointF> *lineData) const;
559 void drawError(QCPPainter *painter, double x, double y, const QCPData &data) const;
560
561 // helper functions:
562 void getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper, int &count) const;
563 void addFillBasePoints(QVector<QPointF> *lineData) const;
564 void removeFillBasePoints(QVector<QPointF> *lineData) const;
565 QPointF lowerFillBasePoint(double lowerKey) const;
566 QPointF upperFillBasePoint(double upperKey) const;
567 const QPolygonF getChannelFillPolygon(const QVector<QPointF> *lineData) const;
568 int findIndexBelowX(const QVector<QPointF> *data, double x) const;
569 int findIndexAboveX(const QVector<QPointF> *data, double x) const;
570 int findIndexBelowY(const QVector<QPointF> *data, double y) const;
571 int findIndexAboveY(const QVector<QPointF> *data, double y) const;
572 double pointDistance(const QPointF &pixelPoint) const;
573 virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
574 virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
575 virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface
576 virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface
577
578 friend class QCustomPlot;
579 friend class QCPLegend;
580 };
581
582 class QCP_LIB_DECL QCPCurve : public QCPAbstractPlottable
583 {
584 Q_OBJECT
585 public:
586 /*!
587 Defines how the curve's line is represented visually in the plot. The line is drawn with the
588 current pen of the curve (\ref setPen).
589 \see setLineStyle
590 */
591 enum LineStyle { lsNone, ///< No line is drawn between data points (e.g. only scatters)
592 lsLine ///< Data points are connected with a straight line
593 };
594 explicit QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis);
595 virtual ~QCPCurve();
596
597 // getters:
598 const QCPCurveDataMap *data() const { return mData; }
599 QCP::ScatterStyle scatterStyle() const { return mScatterStyle; }
600 double scatterSize() const { return mScatterSize; }
601 QPixmap scatterPixmap() const { return mScatterPixmap; }
602 LineStyle lineStyle() const { return mLineStyle; }
603
604 // setters:
605 void setData(QCPCurveDataMap *data, bool copy=false);
606 void setData(const QVector<double> &t, const QVector<double> &key, const QVector<double> &value);
607 void setData(const QVector<double> &key, const QVector<double> &value);
608 void setScatterStyle(QCP::ScatterStyle style);
609 void setScatterSize(double size);
610 void setScatterPixmap(const QPixmap &pixmap);
611 void setLineStyle(LineStyle style);
612
613 // non-property methods:
614 void addData(const QCPCurveDataMap &dataMap);
615 void addData(const QCPCurveData &data);
616 void addData(double t, double key, double value);
617 void addData(double key, double value);
618 void addData(const QVector<double> &ts, const QVector<double> &keys, const QVector<double> &values);
619 void removeDataBefore(double t);
620 void removeDataAfter(double t);
621 void removeData(double fromt, double tot);
622 void removeData(double t);
623 virtual void clearData();
624 virtual double selectTest(const QPointF &pos) const;
625
626 protected:
627 QCPCurveDataMap *mData;
628 QCP::ScatterStyle mScatterStyle;
629 double mScatterSize;
630 QPixmap mScatterPixmap;
631 LineStyle mLineStyle;
632
633 virtual void draw(QCPPainter *painter);
634 virtual void drawLegendIcon(QCPPainter *painter, const QRect &rect) const;
635 // drawing helpers:
636 virtual void drawScatterPlot(QCPPainter *painter, const QVector<QPointF> *pointData) const;
637
638 // helper functions:
639 void getCurveData(QVector<QPointF> *lineData) const;
640 double pointDistance(const QPointF &pixelPoint) const;
641
642 QPointF outsideCoordsToPixels(double key, double value, int region) const;
643 virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
644 virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
645
646 friend class QCustomPlot;
647 friend class QCPLegend;
648 };
649
650 class QCP_LIB_DECL QCPBars : public QCPAbstractPlottable
651 {
652 Q_OBJECT
653 public:
654 explicit QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis);
655 virtual ~QCPBars();
656
657 // getters:
658 double width() const { return mWidth; }
659 QCPBars *barBelow() const { return mBarBelow; }
660 QCPBars *barAbove() const { return mBarAbove; }
661 const QCPBarDataMap *data() const { return mData; }
662
663 // setters:
664 void setWidth(double width);
665 void setData(QCPBarDataMap *data, bool copy=false);
666 void setData(const QVector<double> &key, const QVector<double> &value);
667
668 // non-property methods:
669 void moveBelow(QCPBars *bars);
670 void moveAbove(QCPBars *bars);
671 void addData(const QCPBarDataMap &dataMap);
672 void addData(const QCPBarData &data);
673 void addData(double key, double value);
674 void addData(const QVector<double> &keys, const QVector<double> &values);
675 void removeDataBefore(double key);
676 void removeDataAfter(double key);
677 void removeData(double fromKey, double toKey);
678 void removeData(double key);
679 virtual void clearData();
680 virtual double selectTest(const QPointF &pos) const;
681
682 protected:
683 QCPBarDataMap *mData;
684 double mWidth;
685 QCPBars *mBarBelow, *mBarAbove;
686
687 virtual void draw(QCPPainter *painter);
688 virtual void drawLegendIcon(QCPPainter *painter, const QRect &rect) const;
689
690 QPolygonF getBarPolygon(double key, double value) const;
691 double getBaseValue(double key, bool positive) const;
692 static void connectBars(QCPBars* lower, QCPBars* upper);
693 virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
694 virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
695
696 friend class QCustomPlot;
697 friend class QCPLegend;
698 };
699
700 class QCP_LIB_DECL QCPStatisticalBox : public QCPAbstractPlottable
701 {
702 Q_OBJECT
703 public:
704 explicit QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis);
705 virtual ~QCPStatisticalBox();
706
707 // getters:
708 double key() const { return mKey; }
709 double minimum() const { return mMinimum; }
710 double lowerQuartile() const { return mLowerQuartile; }
711 double median() const { return mMedian; }
712 double upperQuartile() const { return mUpperQuartile; }
713 double maximum() const { return mMaximum; }
714 QVector<double> outliers() const { return mOutliers; }
715 double width() const { return mWidth; }
716 double whiskerWidth() const { return mWhiskerWidth; }
717 QPen whiskerPen() const { return mWhiskerPen; }
718 QPen whiskerBarPen() const { return mWhiskerBarPen; }
719 QPen medianPen() const { return mMedianPen; }
720 double outlierSize() const { return mOutlierSize; }
721 QPen outlierPen() const { return mOutlierPen; }
722 QCP::ScatterStyle outlierStyle() const { return mOutlierStyle; }
723
724 // setters:
725 void setKey(double key);
726 void setMinimum(double value);
727 void setLowerQuartile(double value);
728 void setMedian(double value);
729 void setUpperQuartile(double value);
730 void setMaximum(double value);
731 void setOutliers(const QVector<double> &values);
732 void setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum);
733 void setWidth(double width);
734 void setWhiskerWidth(double width);
735 void setWhiskerPen(const QPen &pen);
736 void setWhiskerBarPen(const QPen &pen);
737 void setMedianPen(const QPen &pen);
738 void setOutlierSize(double pixels);
739 void setOutlierPen(const QPen &pen);
740 void setOutlierStyle(QCP::ScatterStyle style);
741
742 // non-property methods:
743 virtual void clearData();
744 virtual double selectTest(const QPointF &pos) const;
745
746 protected:
747 QVector<double> mOutliers;
748 double mKey, mMinimum, mLowerQuartile, mMedian, mUpperQuartile, mMaximum;
749 double mWidth;
750 double mWhiskerWidth;
751 double mOutlierSize;
752 QPen mWhiskerPen, mWhiskerBarPen, mOutlierPen, mMedianPen;
753 QCP::ScatterStyle mOutlierStyle;
754
755 virtual void draw(QCPPainter *painter);
756 virtual void drawLegendIcon(QCPPainter *painter, const QRect &rect) const;
757
758 virtual void drawQuartileBox(QCPPainter *painter, QRectF *quartileBox=0) const;
759 virtual void drawMedian(QCPPainter *painter) const;
760 virtual void drawWhiskers(QCPPainter *painter) const;
761 virtual void drawOutliers(QCPPainter *painter) const;
762 virtual QCPRange getKeyRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
763 virtual QCPRange getValueRange(bool &validRange, SignDomain inSignDomain=sdBoth) const;
764
765 friend class QCustomPlot;
766 friend class QCPLegend;
767 };
768
769 class QCP_LIB_DECL QCPItemAnchor
770 {
771 public:
772 QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name, int anchorId=-1);
773 virtual ~QCPItemAnchor();
774
775 QString name() const { return mName; }
776 virtual QPointF pixelPoint() const;
777
778 protected:
779 QCustomPlot *mParentPlot;
780 QCPAbstractItem *mParentItem;
781 int mAnchorId;
782 QString mName;
783 // non-property members:
784 QSet<QCPItemPosition*> mChildren;
785
786 void addChild(QCPItemPosition* pos); // called from pos when this anchor is set as parent
787 void removeChild(QCPItemPosition *pos); // called from pos when its parent anchor is reset or pos deleted
788
789 private:
790 Q_DISABLE_COPY(QCPItemAnchor)
791
792 friend class QCPItemPosition;
793 };
794
795 class QCP_LIB_DECL QCPItemPosition : public QCPItemAnchor
796 {
797 public:
798 /*!
799 Defines the ways an item position can be specified. Thus it defines what the numbers passed to
800 \ref setCoords actually mean.
801
802 \see setType
803 */
804 enum PositionType { ptAbsolute ///< Static positioning in pixels, starting from the top left corner of the viewport/widget.
805 ,ptViewportRatio ///< Static positioning given by a ratio of the current viewport (coordinates 0 to 1).
806 ,ptAxisRectRatio ///< Static positioning given by a ratio of the current axis rect (coordinates 0 to 1).
807 ,ptPlotCoords ///< Dynamic positioning at a plot coordinate defined by two axes (see \ref setAxes).
808 };
809
810 QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name);
811 virtual ~QCPItemPosition();
812
813 // getters:
814 PositionType type() const { return mPositionType; }
815 QCPItemAnchor *parentAnchor() const { return mParentAnchor; }
816 double key() const { return mKey; }
817 double value() const { return mValue; }
818 QPointF coords() const { return QPointF(mKey, mValue); }
819 QCPAxis *keyAxis() const { return mKeyAxis; }
820 QCPAxis *valueAxis() const { return mValueAxis; }
821 virtual QPointF pixelPoint() const;
822
823 // setters:
824 void setType(PositionType type);
825 bool setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false);
826 void setCoords(double key, double value);
827 void setCoords(const QPointF &coords);
828 void setAxes(QCPAxis* keyAxis, QCPAxis* valueAxis);
829 void setPixelPoint(const QPointF &pixelPoint);
830
831 protected:
832 PositionType mPositionType;
833 QCPAxis *mKeyAxis, *mValueAxis;
834 double mKey, mValue;
835 QCPItemAnchor *mParentAnchor;
836
837 private:
838 Q_DISABLE_COPY(QCPItemPosition)
839
840 };
841
842 class QCP_LIB_DECL QCPAbstractItem : public QCPLayerable
843 {
844 Q_OBJECT
845 public:
846 QCPAbstractItem(QCustomPlot *parentPlot);
847 virtual ~QCPAbstractItem();
848
849 // getters:
850 bool clipToAxisRect() const { return mClipToAxisRect; }
851 QCPAxis *clipKeyAxis() const { return mClipKeyAxis; }
852 QCPAxis *clipValueAxis() const { return mClipValueAxis; }
853 bool selectable() const { return mSelectable; }
854 bool selected() const { return mSelected; }
855
856 // setters:
857 void setClipToAxisRect(bool clip);
858 void setClipAxes(QCPAxis *keyAxis, QCPAxis *valueAxis);
859 void setClipKeyAxis(QCPAxis *axis);
860 void setClipValueAxis(QCPAxis *axis);
861 void setSelectable(bool selectable);
862 void setSelected(bool selected);
863
864 // non-property methods:
865 virtual double selectTest(const QPointF &pos) const = 0;
866 QList<QCPItemPosition*> positions() const { return mPositions; }
867 QList<QCPItemAnchor*> anchors() const { return mAnchors; }
868 QCPItemPosition *position(const QString &name) const;
869 QCPItemAnchor *anchor(const QString &name) const;
870 bool hasAnchor(const QString &name) const;
871
872 protected:
873 bool mClipToAxisRect;
874 QCPAxis *mClipKeyAxis, *mClipValueAxis;
875 bool mSelectable, mSelected;
876 QList<QCPItemPosition*> mPositions;
877 QList<QCPItemAnchor*> mAnchors;
878
879 virtual QRect clipRect() const;
880 virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
881 virtual void draw(QCPPainter *painter) = 0;
882
883 // helper functions for subclasses:
884 double distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const;
885 double rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const;
886
887 // anchor/position interface:
888 virtual QPointF anchorPixelPoint(int anchorId) const;
889 QCPItemPosition *createPosition(const QString &name);
890 QCPItemAnchor *createAnchor(const QString &name, int anchorId);
891
892 signals:
893 void selectionChanged(bool selected);
894
895 private:
896 Q_DISABLE_COPY(QCPAbstractItem)
897
898 friend class QCustomPlot;
899 friend class QCPItemAnchor;
900 };
901
902 class QCP_LIB_DECL QCPItemStraightLine : public QCPAbstractItem
903 {
904 Q_OBJECT
905 public:
906 QCPItemStraightLine(QCustomPlot *parentPlot);
907 virtual ~QCPItemStraightLine();
908
909 // getters:
910 QPen pen() const { return mPen; }
911 QPen selectedPen() const { return mSelectedPen; }
912
913 // setters;
914 void setPen(const QPen &pen);
915 void setSelectedPen(const QPen &pen);
916
917 // non-property methods:
918 virtual double selectTest(const QPointF &pos) const;
919
920 QCPItemPosition * const point1;
921 QCPItemPosition * const point2;
922
923 protected:
924 QPen mPen, mSelectedPen;
925
926 virtual void draw(QCPPainter *painter);
927
928 // helper functions:
929 double distToStraightLine(const QVector2D &point1, const QVector2D &vec, const QVector2D &point) const;
930 QLineF getRectClippedStraightLine(const QVector2D &point1, const QVector2D &vec, const QRect &rect) const;
931 QPen mainPen() const;
932 };
933
934 class QCP_LIB_DECL QCPItemLine : public QCPAbstractItem
935 {
936 Q_OBJECT
937 public:
938 QCPItemLine(QCustomPlot *parentPlot);
939 virtual ~QCPItemLine();
940
941 // getters:
942 QPen pen() const { return mPen; }
943 QPen selectedPen() const { return mSelectedPen; }
944 QCPLineEnding head() const { return mHead; }
945 QCPLineEnding tail() const { return mTail; }
946
947 // setters;
948 void setPen(const QPen &pen);
949 void setSelectedPen(const QPen &pen);
950 void setHead(const QCPLineEnding &head);
951 void setTail(const QCPLineEnding &tail);
952
953 // non-property methods:
954 virtual double selectTest(const QPointF &pos) const;
955
956 QCPItemPosition * const start;
957 QCPItemPosition * const end;
958
959 protected:
960 QPen mPen, mSelectedPen;
961 QCPLineEnding mHead, mTail;
962
963 virtual void draw(QCPPainter *painter);
964
965 // helper functions:
966 QLineF getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const;
967 QPen mainPen() const;
968 };
969
970 class QCP_LIB_DECL QCPItemEllipse : public QCPAbstractItem
971 {
972 Q_OBJECT
973 public:
974 QCPItemEllipse(QCustomPlot *parentPlot);
975 virtual ~QCPItemEllipse();
976
977 // getters:
978 QPen pen() const { return mPen; }
979 QPen selectedPen() const { return mSelectedPen; }
980 QBrush brush() const { return mBrush; }
981 QBrush selectedBrush() const { return mSelectedBrush; }
982
983 // setters;
984 void setPen(const QPen &pen);
985 void setSelectedPen(const QPen &pen);
986 void setBrush(const QBrush &brush);
987 void setSelectedBrush(const QBrush &brush);
988
989 // non-property methods:
990 virtual double selectTest(const QPointF &pos) const;
991
992 QCPItemPosition * const topLeft;
993 QCPItemPosition * const bottomRight;
994 QCPItemAnchor * const topLeftRim;
995 QCPItemAnchor * const top;
996 QCPItemAnchor * const topRightRim;
997 QCPItemAnchor * const right;
998 QCPItemAnchor * const bottomRightRim;
999 QCPItemAnchor * const bottom;
1000 QCPItemAnchor * const bottomLeftRim;
1001 QCPItemAnchor * const left;
1002
1003 protected:
1004 enum AnchorIndex {aiTopLeftRim, aiTop, aiTopRightRim, aiRight, aiBottomRightRim, aiBottom, aiBottomLeftRim, aiLeft};
1005 QPen mPen, mSelectedPen;
1006 QBrush mBrush, mSelectedBrush;
1007
1008 virtual void draw(QCPPainter *painter);
1009 virtual QPointF anchorPixelPoint(int anchorId) const;
1010
1011 // helper functions:
1012 QPen mainPen() const;
1013 QBrush mainBrush() const;
1014 };
1015
1016 class QCP_LIB_DECL QCPItemRect : public QCPAbstractItem
1017 {
1018 Q_OBJECT
1019 public:
1020 QCPItemRect(QCustomPlot *parentPlot);
1021 virtual ~QCPItemRect();
1022
1023 // getters:
1024 QPen pen() const { return mPen; }
1025 QPen selectedPen() const { return mSelectedPen; }
1026 QBrush brush() const { return mBrush; }
1027 QBrush selectedBrush() const { return mSelectedBrush; }
1028
1029 // setters;
1030 void setPen(const QPen &pen);
1031 void setSelectedPen(const QPen &pen);
1032 void setBrush(const QBrush &brush);
1033 void setSelectedBrush(const QBrush &brush);
1034
1035 // non-property methods:
1036 virtual double selectTest(const QPointF &pos) const;
1037
1038 QCPItemPosition * const topLeft;
1039 QCPItemPosition * const bottomRight;
1040 QCPItemAnchor * const top;
1041 QCPItemAnchor * const topRight;
1042 QCPItemAnchor * const right;
1043 QCPItemAnchor * const bottom;
1044 QCPItemAnchor * const bottomLeft;
1045 QCPItemAnchor * const left;
1046
1047 protected:
1048 enum AnchorIndex {aiTop, aiTopRight, aiRight, aiBottom, aiBottomLeft, aiLeft};
1049 QPen mPen, mSelectedPen;
1050 QBrush mBrush, mSelectedBrush;
1051
1052 virtual void draw(QCPPainter *painter);
1053 virtual QPointF anchorPixelPoint(int anchorId) const;
1054
1055 // helper functions:
1056 QPen mainPen() const;
1057 QBrush mainBrush() const;
1058 };
1059
1060 class QCP_LIB_DECL QCPItemPixmap : public QCPAbstractItem
1061 {
1062 Q_OBJECT
1063 public:
1064 QCPItemPixmap(QCustomPlot *parentPlot);
1065 virtual ~QCPItemPixmap();
1066
1067 // getters:
1068 QPixmap pixmap() const { return mPixmap; }
1069 bool scaled() const { return mScaled; }
1070 Qt::AspectRatioMode aspectRatioMode() const { return mAspectRatioMode; }
1071 QPen pen() const { return mPen; }
1072 QPen selectedPen() const { return mSelectedPen; }
1073
1074 // setters;
1075 void setPixmap(const QPixmap &pixmap);
1076 void setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode=Qt::KeepAspectRatio);
1077 void setPen(const QPen &pen);
1078 void setSelectedPen(const QPen &pen);
1079
1080 // non-property methods:
1081 virtual double selectTest(const QPointF &pos) const;
1082
1083 QCPItemPosition * const topLeft;
1084 QCPItemPosition * const bottomRight;
1085 QCPItemAnchor * const top;
1086 QCPItemAnchor * const topRight;
1087 QCPItemAnchor * const right;
1088 QCPItemAnchor * const bottom;
1089 QCPItemAnchor * const bottomLeft;
1090 QCPItemAnchor * const left;
1091
1092 protected:
1093 enum AnchorIndex {aiTop, aiTopRight, aiRight, aiBottom, aiBottomLeft, aiLeft};
1094 QPixmap mPixmap;
1095 QPixmap mScaledPixmap;
1096 bool mScaled;
1097 Qt::AspectRatioMode mAspectRatioMode;
1098 QPen mPen, mSelectedPen;
1099
1100 virtual void draw(QCPPainter *painter);
1101 virtual QPointF anchorPixelPoint(int anchorId) const;
1102
1103 // helper functions:
1104 void updateScaledPixmap(QRect finalRect=QRect(), bool flipHorz=false, bool flipVert=false);
1105 QRect getFinalRect(bool *flippedHorz=0, bool *flippedVert=0) const;
1106 QPen mainPen() const;
1107 };
1108
1109 class QCP_LIB_DECL QCPItemText : public QCPAbstractItem
1110 {
1111 Q_OBJECT
1112 public:
1113 QCPItemText(QCustomPlot *parentPlot);
1114 virtual ~QCPItemText();
1115
1116 // getters:
1117 QColor color() const { return mColor; }
1118 QColor selectedColor() const { return mSelectedColor; }
1119 QPen pen() const { return mPen; }
1120 QPen selectedPen() const { return mSelectedPen; }
1121 QBrush brush() const { return mBrush; }
1122 QBrush selectedBrush() const { return mSelectedBrush; }
1123 QFont font() const { return mFont; }
1124 QFont selectedFont() const { return mSelectedFont; }
1125 QString text() const { return mText; }
1126 Qt::Alignment positionAlignment() const { return mPositionAlignment; }
1127 Qt::Alignment textAlignment() const { return mTextAlignment; }
1128 double rotation() const { return mRotation; }
1129 QMargins padding() const { return mPadding; }
1130
1131 // setters;
1132 void setColor(const QColor &color);
1133 void setSelectedColor(const QColor &color);
1134 void setPen(const QPen &pen);
1135 void setSelectedPen(const QPen &pen);
1136 void setBrush(const QBrush &brush);
1137 void setSelectedBrush(const QBrush &brush);
1138 void setFont(const QFont &font);
1139 void setSelectedFont(const QFont &font);
1140 void setText(const QString &text);
1141 void setPositionAlignment(Qt::Alignment alignment);
1142 void setTextAlignment(Qt::Alignment alignment);
1143 void setRotation(double degrees);
1144 void setPadding(const QMargins &padding);
1145
1146 // non-property methods:
1147 virtual double selectTest(const QPointF &pos) const;
1148
1149 QCPItemPosition * const position;
1150 QCPItemAnchor * const topLeft;
1151 QCPItemAnchor * const top;
1152 QCPItemAnchor * const topRight;
1153 QCPItemAnchor * const right;
1154 QCPItemAnchor * const bottomRight;
1155 QCPItemAnchor * const bottom;
1156 QCPItemAnchor * const bottomLeft;
1157 QCPItemAnchor * const left;
1158
1159 protected:
1160 enum AnchorIndex {aiTopLeft, aiTop, aiTopRight, aiRight, aiBottomRight, aiBottom, aiBottomLeft, aiLeft};
1161 QColor mColor, mSelectedColor;
1162 QPen mPen, mSelectedPen;
1163 QBrush mBrush, mSelectedBrush;
1164 QFont mFont, mSelectedFont;
1165 QString mText;
1166 Qt::Alignment mPositionAlignment;
1167 Qt::Alignment mTextAlignment;
1168 double mRotation;
1169 QMargins mPadding;
1170
1171 virtual void draw(QCPPainter *painter);
1172 virtual QPointF anchorPixelPoint(int anchorId) const;
1173
1174 // helper functions:
1175 QPointF getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const;
1176 QFont mainFont() const;
1177 QColor mainColor() const;
1178 QPen mainPen() const;
1179 QBrush mainBrush() const;
1180 };
1181
1182 class QCP_LIB_DECL QCPItemCurve : public QCPAbstractItem
1183 {
1184 Q_OBJECT
1185 public:
1186 QCPItemCurve(QCustomPlot *parentPlot);
1187 virtual ~QCPItemCurve();
1188
1189 // getters:
1190 QPen pen() const { return mPen; }
1191 QPen selectedPen() const { return mSelectedPen; }
1192 QCPLineEnding head() const { return mHead; }
1193 QCPLineEnding tail() const { return mTail; }
1194
1195 // setters;
1196 void setPen(const QPen &pen);
1197 void setSelectedPen(const QPen &pen);
1198 void setHead(const QCPLineEnding &head);
1199 void setTail(const QCPLineEnding &tail);
1200
1201 // non-property methods:
1202 virtual double selectTest(const QPointF &pos) const;
1203
1204 QCPItemPosition * const start;
1205 QCPItemPosition * const startDir;
1206 QCPItemPosition * const endDir;
1207 QCPItemPosition * const end;
1208
1209 protected:
1210 QPen mPen, mSelectedPen;
1211 QCPLineEnding mHead, mTail;
1212
1213 virtual void draw(QCPPainter *painter);
1214
1215 // helper functions:
1216 QPen mainPen() const;
1217 };
1218
1219 class QCP_LIB_DECL QCPItemBracket : public QCPAbstractItem
1220 {
1221 Q_OBJECT
1222 public:
1223 enum BracketStyle { bsSquare ///< A brace with angled edges
1224 ,bsRound ///< A brace with round edges
1225 ,bsCurly ///< A curly brace
1226 ,bsCalligraphic ///< A curly brace with varying stroke width giving a calligraphic impression
1227 };
1228
1229 QCPItemBracket(QCustomPlot *parentPlot);
1230 virtual ~QCPItemBracket();
1231
1232 // getters:
1233 QPen pen() const { return mPen; }
1234 QPen selectedPen() const { return mSelectedPen; }
1235 double length() const { return mLength; }
1236 BracketStyle style() const { return mStyle; }
1237
1238 // setters;
1239 void setPen(const QPen &pen);
1240 void setSelectedPen(const QPen &pen);
1241 void setLength(double length);
1242 void setStyle(BracketStyle style);
1243
1244 // non-property methods:
1245 virtual double selectTest(const QPointF &pos) const;
1246
1247 QCPItemPosition * const left;
1248 QCPItemPosition * const right;
1249 QCPItemAnchor * const center;
1250
1251 protected:
1252 enum AnchorIndex {aiCenter};
1253 QPen mPen, mSelectedPen;
1254 double mLength;
1255 BracketStyle mStyle;
1256
1257 virtual void draw(QCPPainter *painter);
1258 virtual QPointF anchorPixelPoint(int anchorId) const;
1259
1260 // helper functions:
1261 QPen mainPen() const;
1262 };
1263
1264 class QCP_LIB_DECL QCPItemTracer : public QCPAbstractItem
1265 {
1266 Q_OBJECT
1267 public:
1268 /*!
1269 The different visual appearances a tracer item can have. Some styles size may be controlled with \ref setSize.
1270
1271 \see setStyle
1272 */
1273 enum TracerStyle { tsNone ///< The tracer is not visible
1274 ,tsPlus ///< A plus shaped crosshair with limited size
1275 ,tsCrosshair ///< A plus shaped crosshair which spans the complete axis rect
1276 ,tsCircle ///< A circle
1277 ,tsSquare ///< A square
1278 };
1279 Q_ENUMS(TracerStyle)
1280
1281 QCPItemTracer(QCustomPlot *parentPlot);
1282 virtual ~QCPItemTracer();
1283
1284 // getters:
1285 QPen pen() const { return mPen; }
1286 QPen selectedPen() const { return mSelectedPen; }
1287 QBrush brush() const { return mBrush; }
1288 QBrush selectedBrush() const { return mSelectedBrush; }
1289 double size() const { return mSize; }
1290 TracerStyle style() const { return mStyle; }
1291 QCPGraph *graph() const { return mGraph; }
1292 double graphKey() const { return mGraphKey; }
1293 bool interpolating() const { return mInterpolating; }
1294
1295 // setters;
1296 void setPen(const QPen &pen);
1297 void setSelectedPen(const QPen &pen);
1298 void setBrush(const QBrush &brush);
1299 void setSelectedBrush(const QBrush &brush);
1300 void setSize(double size);
1301 void setStyle(TracerStyle style);
1302 void setGraph(QCPGraph *graph);
1303 void setGraphKey(double key);
1304 void setInterpolating(bool enabled);
1305
1306 // non-property methods:
1307 virtual double selectTest(const QPointF &pos) const;
1308 void updatePosition();
1309
1310 QCPItemPosition * const position;
1311
1312 protected:
1313 QPen mPen, mSelectedPen;
1314 QBrush mBrush, mSelectedBrush;
1315 double mSize;
1316 TracerStyle mStyle;
1317 QCPGraph *mGraph;
1318 double mGraphKey;
1319 bool mInterpolating;
1320
1321 virtual void draw(QCPPainter *painter);
1322
1323 // helper functions:
1324 QPen mainPen() const;
1325 QBrush mainBrush() const;
1326 };
1327
1328 class QCP_LIB_DECL QCPRange
1329 {
1330 public:
1331 double lower, upper;
1332 QCPRange();
1333 QCPRange(double lower, double upper);
1334 double size() const;
1335 double center() const;
1336 void normalize();
1337 QCPRange sanitizedForLogScale() const;
1338 QCPRange sanitizedForLinScale() const;
1339 bool contains(double value) const;
1340
1341 static bool validRange(double lower, double upper);
1342 static bool validRange(const QCPRange &range);
1343 static const double minRange; //1e-280;
1344 static const double maxRange; //1e280;
1345 };
1346 Q_DECLARE_TYPEINFO(QCPRange, Q_MOVABLE_TYPE);
1347
1348 class QCP_LIB_DECL QCPAbstractLegendItem : public QObject
1349 {
1350 Q_OBJECT
1351 public:
1352 QCPAbstractLegendItem(QCPLegend *parent);
1353 virtual ~QCPAbstractLegendItem() {}
1354
1355 // getters:
1356 bool antialiased() const { return mAntialiased; }
1357 QFont font() const { return mFont; }
1358 QColor textColor() const { return mTextColor; }
1359 QFont selectedFont() const { return mSelectedFont; }
1360 QColor selectedTextColor() const { return mSelectedTextColor; }
1361 bool selectable() const { return mSelectable; }
1362 bool selected() const { return mSelected; }
1363
1364 // setters:
1365 void setAntialiased(bool enabled);
1366 void setFont(const QFont &font);
1367 void setTextColor(const QColor &color);
1368 void setSelectedFont(const QFont &font);
1369 void setSelectedTextColor(const QColor &color);
1370 void setSelectable(bool selectable);
1371 void setSelected(bool selected);
1372
1373 signals:
1374 void selectionChanged(bool selected);
1375
1376 protected:
1377 QCPLegend *mParentLegend;
1378 bool mAntialiased;
1379 QFont mFont;
1380 QColor mTextColor;
1381 QFont mSelectedFont;
1382 QColor mSelectedTextColor;
1383 bool mSelectable, mSelected;
1384
1385 virtual void draw(QCPPainter *painter, const QRect &rect) const = 0;
1386 virtual QSize size(const QSize &targetSize) const = 0;
1387 void applyAntialiasingHint(QCPPainter *painter) const;
1388
1389 private:
1390 Q_DISABLE_COPY(QCPAbstractLegendItem)
1391
1392 friend class QCPLegend;
1393 };
1394
1395 class QCP_LIB_DECL QCPPlottableLegendItem : public QCPAbstractLegendItem
1396 {
1397 Q_OBJECT
1398 public:
1399 QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable);
1400 virtual ~QCPPlottableLegendItem() {}
1401
1402 // getters:
1403 QCPAbstractPlottable *plottable() { return mPlottable; }
1404 bool textWrap() const { return mTextWrap; }
1405
1406 // setters:
1407 void setTextWrap(bool wrap);
1408
1409 protected:
1410 QCPAbstractPlottable *mPlottable;
1411 bool mTextWrap;
1412
1413 QPen getIconBorderPen() const;
1414 QColor getTextColor() const;
1415 QFont getFont() const;
1416
1417 virtual void draw(QCPPainter *painter, const QRect &rect) const;
1418 virtual QSize size(const QSize &targetSize) const;
1419 };
1420
1421 class QCP_LIB_DECL QCPLegend : public QCPLayerable
1422 {
1423 Q_OBJECT
1424 public:
1425 /*!
1426 Defines where the legend is positioned inside the QCustomPlot axis rect.
1427 */
1428 enum PositionStyle { psManual ///< Position is not changed automatically. Set manually via \ref setPosition
1429 ,psTopLeft ///< Legend is positioned in the top left corner of the axis rect with distance to the border corresponding to the currently set top and left margins
1430 ,psTop ///< Legend is horizontally centered at the top of the axis rect with distance to the border corresponding to the currently set top margin
1431 ,psTopRight ///< Legend is positioned in the top right corner of the axis rect with distance to the border corresponding to the currently set top and right margins
1432 ,psRight ///< Legend is vertically centered at the right of the axis rect with distance to the border corresponding to the currently set right margin
1433 ,psBottomRight ///< Legend is positioned in the bottom right corner of the axis rect with distance to the border corresponding to the currently set bottom and right margins
1434 ,psBottom ///< Legend is horizontally centered at the bottom of the axis rect with distance to the border corresponding to the currently set bottom margin
1435 ,psBottomLeft ///< Legend is positioned in the bottom left corner of the axis rect with distance to the border corresponding to the currently set bottom and left margins
1436 ,psLeft ///< Legend is vertically centered at the left of the axis rect with distance to the border corresponding to the currently set left margin
1437 };
1438 Q_ENUMS(PositionStyle)
1439
1440 /*!
1441 Defines the selectable parts of a legend
1442 */
1443 enum SelectablePart { spNone = 0 ///< None
1444 ,spLegendBox = 0x001 ///< The legend box (frame)
1445 ,spItems = 0x002 ///< Legend items individually (see \ref selectedItems)
1446 };
1447 Q_ENUMS(SelectablePart)
1448 Q_DECLARE_FLAGS(SelectableParts, SelectablePart)
1449
1450 explicit QCPLegend(QCustomPlot *parentPlot);
1451 virtual ~QCPLegend();
1452
1453 // getters:
1454 QPen borderPen() const { return mBorderPen; }
1455 QBrush brush() const { return mBrush; }
1456 QFont font() const { return mFont; }
1457 QColor textColor() const { return mTextColor; }
1458 PositionStyle positionStyle() const { return mPositionStyle; }
1459 QPoint position() const { return mPosition; }
1460 bool autoSize() const { return mAutoSize; }
1461 QSize size() const { return mSize; }
1462 QSize minimumSize() const { return mMinimumSize; }
1463 int paddingLeft() const { return mPaddingLeft; }
1464 int paddingRight() const { return mPaddingRight; }
1465 int paddingTop() const { return mPaddingTop; }
1466 int paddingBottom() const { return mPaddingBottom; }
1467 int marginLeft() const { return mMarginLeft; }
1468 int marginRight() const { return mMarginRight; }
1469 int marginTop() const { return mMarginTop; }
1470 int marginBottom() const { return mMarginBottom; }
1471 int itemSpacing() const { return mItemSpacing; }
1472 QSize iconSize() const { return mIconSize; }
1473 int iconTextPadding() const { return mIconTextPadding; }
1474 QPen iconBorderPen() const { return mIconBorderPen; }
1475 SelectableParts selectable() const { return mSelectable; }
1476 SelectableParts selected() const { return mSelected; }
1477 QPen selectedBorderPen() const { return mSelectedBorderPen; }
1478 QPen selectedIconBorderPen() const { return mSelectedIconBorderPen; }
1479 QBrush selectedBrush() const { return mSelectedBrush; }
1480 QFont selectedFont() const { return mSelectedFont; }
1481 QColor selectedTextColor() const { return mSelectedTextColor; }
1482
1483 // setters:
1484 void setBorderPen(const QPen &pen);
1485 void setBrush(const QBrush &brush);
1486 void setFont(const QFont &font);
1487 void setTextColor(const QColor &color);
1488 void setPositionStyle(PositionStyle legendPositionStyle);
1489 void setPosition(const QPoint &pixelPosition);
1490 void setAutoSize(bool on);
1491 void setSize(const QSize &size);
1492 void setSize(int width, int height);
1493 void setMinimumSize(const QSize &size);
1494 void setMinimumSize(int width, int height);
1495 void setPaddingLeft(int padding);
1496 void setPaddingRight(int padding);
1497 void setPaddingTop(int padding);
1498 void setPaddingBottom(int padding);
1499 void setPadding(int left, int right, int top, int bottom);
1500 void setMarginLeft(int margin);
1501 void setMarginRight(int margin);
1502 void setMarginTop(int margin);
1503 void setMarginBottom(int margin);
1504 void setMargin(int left, int right, int top, int bottom);
1505 void setItemSpacing(int spacing);
1506 void setIconSize(const QSize &size);
1507 void setIconSize(int width, int height);
1508 void setIconTextPadding(int padding);
1509 void setIconBorderPen(const QPen &pen);
1510 void setSelectable(const SelectableParts &selectable);
1511 void setSelected(const SelectableParts &selected);
1512 void setSelectedBorderPen(const QPen &pen);
1513 void setSelectedIconBorderPen(const QPen &pen);
1514 void setSelectedBrush(const QBrush &brush);
1515 void setSelectedFont(const QFont &font);
1516 void setSelectedTextColor(const QColor &color);
1517
1518 // non-property methods:
1519 QCPAbstractLegendItem *item(int index) const;
1520 QCPPlottableLegendItem *itemWithPlottable(const QCPAbstractPlottable *plottable) const;
1521 int itemCount() const;
1522 bool hasItem(QCPAbstractLegendItem *item) const;
1523 bool hasItemWithPlottable(const QCPAbstractPlottable *plottable) const;
1524 bool addItem(QCPAbstractLegendItem *item);
1525 bool removeItem(int index);
1526 bool removeItem(QCPAbstractLegendItem *item);
1527 void clearItems();
1528 QList<QCPAbstractLegendItem*> selectedItems() const;
1529 void reArrange();
1530
1531 bool selectTestLegend(const QPointF &pos) const;
1532 QCPAbstractLegendItem *selectTestItem(const QPoint pos) const;
1533
1534 signals:
1535 void selectionChanged(QCPLegend::SelectableParts selection);
1536
1537 protected:
1538 // simple properties with getters and setters:
1539 QPen mBorderPen, mIconBorderPen;
1540 QBrush mBrush;
1541 QFont mFont;
1542 QColor mTextColor;
1543 QPoint mPosition;
1544 QSize mSize, mMinimumSize, mIconSize;
1545 PositionStyle mPositionStyle;
1546 bool mAutoSize;
1547 int mPaddingLeft, mPaddingRight, mPaddingTop, mPaddingBottom;
1548 int mMarginLeft, mMarginRight, mMarginTop, mMarginBottom;
1549 int mItemSpacing, mIconTextPadding;
1550 SelectableParts mSelected, mSelectable;
1551 QPen mSelectedBorderPen, mSelectedIconBorderPen;
1552 QBrush mSelectedBrush;
1553 QFont mSelectedFont;
1554 QColor mSelectedTextColor;
1555
1556 // internal or not explicitly exposed properties:
1557 QList<QCPAbstractLegendItem*> mItems;
1558 QMap<QCPAbstractLegendItem*, QRect> mItemBoundingBoxes;
1559
1560 virtual void updateSelectionState();
1561 virtual bool handleLegendSelection(QMouseEvent *event, bool additiveSelection, bool &modified);
1562 // introduced methods:
1563 virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
1564 virtual void draw(QCPPainter *painter);
1565 virtual void calculateAutoSize();
1566 virtual void calculateAutoPosition();
1567
1568 // drawing helpers:
1569 QPen getBorderPen() const;
1570 QBrush getBrush() const;
1571
1572 private:
1573 Q_DISABLE_COPY(QCPLegend)
1574
1575 friend class QCustomPlot;
1576 friend class QCPAbstractLegendItem;
1577 };
1578 Q_DECLARE_OPERATORS_FOR_FLAGS(QCPLegend::SelectableParts)
1579
1580 class QCP_LIB_DECL QCPGrid : public QCPLayerable
1581 {
1582 Q_OBJECT
1583 public:
1584 QCPGrid(QCPAxis *parentAxis);
1585 ~QCPGrid();
1586
1587 // getters:
1588 bool subGridVisible() const { return mSubGridVisible; }
1589 bool antialiasedSubGrid() const { return mAntialiasedSubGrid; }
1590 bool antialiasedZeroLine() const { return mAntialiasedZeroLine; }
1591 QPen pen() const { return mPen; }
1592 QPen subGridPen() const { return mSubGridPen; }
1593 QPen zeroLinePen() const { return mZeroLinePen; }
1594
1595 // setters:
1596 void setSubGridVisible(bool visible);
1597 void setAntialiasedSubGrid(bool enabled);
1598 void setAntialiasedZeroLine(bool enabled);
1599 void setPen(const QPen &pen);
1600 void setSubGridPen(const QPen &pen);
1601 void setZeroLinePen(const QPen &pen);
1602
1603 protected:
1604 QCPAxis *mParentAxis;
1605 bool mSubGridVisible;
1606 bool mAntialiasedSubGrid, mAntialiasedZeroLine;
1607 QPen mPen, mSubGridPen, mZeroLinePen;
1608
1609 virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
1610 virtual void draw(QCPPainter *painter);
1611 // drawing helpers:
1612 void drawGridLines(QCPPainter *painter) const;
1613 void drawSubGridLines(QCPPainter *painter) const;
1614
1615 friend class QCPAxis;
1616 };
1617
1618 class QCP_LIB_DECL QCPAxis : public QCPLayerable
1619 {
1620 Q_OBJECT
1621 /// \cond INCLUDE_QPROPERTIES
1622 Q_PROPERTY(AxisType axisType READ axisType WRITE setAxisType)
1623 Q_PROPERTY(ScaleType scaleType READ scaleType WRITE setScaleType)
1624 Q_PROPERTY(double scaleLogBase READ scaleLogBase WRITE setScaleLogBase)
1625 Q_PROPERTY(QRect axisRect READ axisRect WRITE setAxisRect)
1626 Q_PROPERTY(QCPRange range READ range WRITE setRange)
1627 Q_PROPERTY(bool grid READ grid WRITE setGrid)
1628 Q_PROPERTY(bool subGrid READ subGrid WRITE setSubGrid)
1629 Q_PROPERTY(bool autoTicks READ autoTicks WRITE setAutoTicks)
1630 Q_PROPERTY(int autoTickCount READ autoTickCount WRITE setAutoTickCount)
1631 Q_PROPERTY(bool autoTickLabels READ autoTickLabels WRITE setAutoTickLabels)
1632 Q_PROPERTY(bool autoTickStep READ autoTickStep WRITE setAutoTickStep)
1633 Q_PROPERTY(bool autoSubTicks READ autoSubTicks WRITE setAutoSubTicks)
1634 Q_PROPERTY(bool ticks READ ticks WRITE setTicks)
1635 Q_PROPERTY(bool tickLabels READ tickLabels WRITE setTickLabels)
1636 Q_PROPERTY(int tickLabelPadding READ tickLabelPadding WRITE setTickLabelPadding)
1637 Q_PROPERTY(LabelType tickLabelType READ tickLabelType WRITE setTickLabelType)
1638 Q_PROPERTY(QFont tickLabelFont READ tickLabelFont WRITE setTickLabelFont)
1639 Q_PROPERTY(double tickLabelRotation READ tickLabelRotation WRITE setTickLabelRotation)
1640 Q_PROPERTY(QString dateTimeFormat READ dateTimeFormat WRITE setDateTimeFormat)
1641 Q_PROPERTY(QString numberFormat READ numberFormat WRITE setNumberFormat)
1642 Q_PROPERTY(double tickStep READ tickStep WRITE setTickStep)
1643 Q_PROPERTY(QVector<double> tickVector READ tickVector WRITE setTickVector)
1644 Q_PROPERTY(QVector<QString> tickVectorLabels READ tickVectorLabels WRITE setTickVectorLabels)
1645 Q_PROPERTY(int subTickCount READ subTickCount WRITE setSubTickCount)
1646 Q_PROPERTY(QPen basePen READ basePen WRITE setBasePen)
1647 Q_PROPERTY(QPen gridPen READ gridPen WRITE setGridPen)
1648 Q_PROPERTY(QPen subGridPen READ subGridPen WRITE setSubGridPen)
1649 Q_PROPERTY(QPen tickPen READ tickPen WRITE setTickPen)
1650 Q_PROPERTY(QPen subTickPen READ subTickPen WRITE setSubTickPen)
1651 Q_PROPERTY(QFont labelFont READ labelFont WRITE setLabelFont)
1652 Q_PROPERTY(QString label READ label WRITE setLabel)
1653 Q_PROPERTY(int labelPadding READ labelPadding WRITE setLabelPadding)
1654 /// \endcond
1655 public:
1656 /*!
1657 Defines at which side of the axis rect the axis will appear. This also affects how the tick
1658 marks are drawn, on which side the labels are placed etc.
1659 \see setAxisType
1660 */
1661 enum AxisType { atLeft ///< Axis is vertical and on the left side of the axis rect of the parent QCustomPlot
1662 ,atRight ///< Axis is vertical and on the right side of the axis rect of the parent QCustomPlot
1663 ,atTop ///< Axis is horizontal and on the top side of the axis rect of the parent QCustomPlot
1664 ,atBottom ///< Axis is horizontal and on the bottom side of the axis rect of the parent QCustomPlot
1665 };
1666 Q_ENUMS(AxisType)
1667 /*!
1668 When automatic tick label generation is enabled (\ref setAutoTickLabels), defines how the
1669 numerical value (coordinate) of the tick position is translated into a string that will be
1670 drawn at the tick position.
1671 \see setTickLabelType
1672 */
1673 enum LabelType { ltNumber ///< Tick coordinate is regarded as normal number and will be displayed as such. (see \ref setNumberFormat)
1674 ,ltDateTime ///< Tick coordinate is regarded as a date/time (seconds since 1970-01-01T00:00:00 UTC, see QDateTime::toTime_t) and will be displayed and formatted as such. (see \ref setDateTimeFormat)
1675 };
1676 Q_ENUMS(LabelType)
1677 /*!
1678 Defines the scale of an axis.
1679 \see setScaleType
1680 */
1681 enum ScaleType { stLinear ///< Normal linear scaling
1682 ,stLogarithmic ///< Logarithmic scaling with correspondingly transformed plots and (major) tick marks at every base power (see \ref setScaleLogBase).
1683 };
1684 Q_ENUMS(ScaleType)
1685 /*!
1686 Defines the selectable parts of an axis.
1687 \see setSelectable, setSelected
1688 */
1689 enum SelectablePart { spNone = 0 ///< None of the selectable parts
1690 ,spAxis = 0x001 ///< The axis backbone and tick marks
1691 ,spTickLabels = 0x002 ///< Tick labels (numbers) of this axis (as a whole, not individually)
1692 ,spAxisLabel = 0x004 ///< The axis label
1693 };
1694 Q_ENUMS(SelectablePart)
1695 Q_DECLARE_FLAGS(SelectableParts, SelectablePart)
1696
1697 explicit QCPAxis(QCustomPlot *parentPlot, AxisType type);
1698 virtual ~QCPAxis();
1699
1700 // getters:
1701 AxisType axisType() const { return mAxisType; }
1702 QRect axisRect() const { return mAxisRect; }
1703 ScaleType scaleType() const { return mScaleType; }
1704 double scaleLogBase() const { return mScaleLogBase; }
1705 const QCPRange range() const { return mRange; }
1706 bool rangeReversed() const { return mRangeReversed; }
1707 bool antialiasedGrid() const { return mGrid->antialiased(); }
1708 bool antialiasedSubGrid() const { return mGrid->antialiasedSubGrid(); }
1709 bool antialiasedZeroLine() const { return mGrid->antialiasedZeroLine(); }
1710 bool grid() const { return mGrid->visible(); }
1711 bool subGrid() const { return mGrid->subGridVisible(); }
1712 bool autoTicks() const { return mAutoTicks; }
1713 int autoTickCount() const { return mAutoTickCount; }
1714 bool autoTickLabels() const { return mAutoTickLabels; }
1715 bool autoTickStep() const { return mAutoTickStep; }
1716 bool autoSubTicks() const { return mAutoSubTicks; }
1717 bool ticks() const { return mTicks; }
1718 bool tickLabels() const { return mTickLabels; }
1719 int tickLabelPadding() const { return mTickLabelPadding; }
1720 LabelType tickLabelType() const { return mTickLabelType; }
1721 QFont tickLabelFont() const { return mTickLabelFont; }
1722 QColor tickLabelColor() const { return mTickLabelColor; }
1723 double tickLabelRotation() const { return mTickLabelRotation; }
1724 QString dateTimeFormat() const { return mDateTimeFormat; }
1725 QString numberFormat() const;
1726 int numberPrecision() const { return mNumberPrecision; }
1727 double tickStep() const { return mTickStep; }
1728 QVector<double> tickVector() const { return mTickVector; }
1729 QVector<QString> tickVectorLabels() const { return mTickVectorLabels; }
1730 int tickLengthIn() const { return mTickLengthIn; }
1731 int tickLengthOut() const { return mTickLengthOut; }
1732 int subTickCount() const { return mSubTickCount; }
1733 int subTickLengthIn() const { return mSubTickLengthIn; }
1734 int subTickLengthOut() const { return mSubTickLengthOut; }
1735 QPen basePen() const { return mBasePen; }
1736 QPen gridPen() const { return mGrid->pen(); }
1737 QPen subGridPen() const { return mGrid->subGridPen(); }
1738 QPen zeroLinePen() const { return mGrid->zeroLinePen(); }
1739 QPen tickPen() const { return mTickPen; }
1740 QPen subTickPen() const { return mSubTickPen; }
1741 QFont labelFont() const { return mLabelFont; }
1742 QColor labelColor() const { return mLabelColor; }
1743 QString label() const { return mLabel; }
1744 int labelPadding() const { return mLabelPadding; }
1745 int padding() const { return mPadding; }
1746 SelectableParts selected() const { return mSelected; }
1747 SelectableParts selectable() const { return mSelectable; }
1748 QFont selectedTickLabelFont() const { return mSelectedTickLabelFont; }
1749 QFont selectedLabelFont() const { return mSelectedLabelFont; }
1750 QColor selectedTickLabelColor() const { return mSelectedTickLabelColor; }
1751 QColor selectedLabelColor() const { return mSelectedLabelColor; }
1752 QPen selectedBasePen() const { return mSelectedBasePen; }
1753 QPen selectedTickPen() const { return mSelectedTickPen; }
1754 QPen selectedSubTickPen() const { return mSelectedSubTickPen; }
1755
1756 // setters:
1757 void setScaleType(ScaleType type);
1758 void setScaleLogBase(double base);
1759 void setRange(double lower, double upper);
1760 void setRange(double position, double size, Qt::AlignmentFlag alignment);
1761 void setRangeLower(double lower);
1762 void setRangeUpper(double upper);
1763 void setRangeReversed(bool reversed);
1764 void setAntialiasedGrid(bool enabled);
1765 void setAntialiasedSubGrid(bool enabled);
1766 void setAntialiasedZeroLine(bool enabled);
1767 void setGrid(bool show);
1768 void setSubGrid(bool show);
1769 void setAutoTicks(bool on);
1770 void setAutoTickCount(int approximateCount);
1771 void setAutoTickLabels(bool on);
1772 void setAutoTickStep(bool on);
1773 void setAutoSubTicks(bool on);
1774 void setTicks(bool show);
1775 void setTickLabels(bool show);
1776 void setTickLabelPadding(int padding);
1777 void setTickLabelType(LabelType type);
1778 void setTickLabelFont(const QFont &font);
1779 void setTickLabelColor(const QColor &color);
1780 void setTickLabelRotation(double degrees);
1781 void setDateTimeFormat(const QString &format);
1782 void setNumberFormat(const QString &formatCode);
1783 void setNumberPrecision(int precision);
1784 void setTickStep(double step);
1785 void setTickVector(const QVector<double> &vec);
1786 void setTickVectorLabels(const QVector<QString> &vec);
1787 void setTickLength(int inside, int outside=0);
1788 void setSubTickCount(int count);
1789 void setSubTickLength(int inside, int outside=0);
1790 void setBasePen(const QPen &pen);
1791 void setGridPen(const QPen &pen);
1792 void setSubGridPen(const QPen &pen);
1793 void setZeroLinePen(const QPen &pen);
1794 void setTickPen(const QPen &pen);
1795 void setSubTickPen(const QPen &pen);
1796 void setLabelFont(const QFont &font);
1797 void setLabelColor(const QColor &color);
1798 void setLabel(const QString &str);
1799 void setLabelPadding(int padding);
1800 void setPadding(int padding);
1801 void setSelectedTickLabelFont(const QFont &font);
1802 void setSelectedLabelFont(const QFont &font);
1803 void setSelectedTickLabelColor(const QColor &color);
1804 void setSelectedLabelColor(const QColor &color);
1805 void setSelectedBasePen(const QPen &pen);
1806 void setSelectedTickPen(const QPen &pen);
1807 void setSelectedSubTickPen(const QPen &pen);
1808
1809 // non-property methods:
1810 Qt::Orientation orientation() const { return mOrientation; }
1811 void moveRange(double diff);
1812 void scaleRange(double factor, double center);
1813 void setScaleRatio(const QCPAxis *otherAxis, double ratio=1.0);
1814 double pixelToCoord(double value) const;
1815 double coordToPixel(double value) const;
1816 SelectablePart selectTest(const QPointF &pos) const;
1817
1818 public slots:
1819 // slot setters:
1820 void setRange(const QCPRange &range);
1821 void setSelectable(const QCPAxis::SelectableParts &selectable);
1822 void setSelected(const QCPAxis::SelectableParts &selected);
1823
1824 signals:
1825 void ticksRequest();
1826 void rangeChanged(const QCPRange &newRange);
1827 void selectionChanged(QCPAxis::SelectableParts selection);
1828
1829 protected:
1830 // simple properties with getters and setters:
1831 QVector<double> mTickVector;
1832 QVector<QString> mTickVectorLabels;
1833 QCPRange mRange;
1834 QString mDateTimeFormat;
1835 QString mLabel;
1836 QRect mAxisRect;
1837 QPen mBasePen, mTickPen, mSubTickPen;
1838 QFont mTickLabelFont, mLabelFont;
1839 QColor mTickLabelColor, mLabelColor;
1840 LabelType mTickLabelType;
1841 ScaleType mScaleType;
1842 AxisType mAxisType;
1843 double mTickStep;
1844 double mScaleLogBase, mScaleLogBaseLogInv;
1845 int mSubTickCount, mTickLengthIn, mTickLengthOut, mSubTickLengthIn, mSubTickLengthOut;
1846 int mAutoTickCount;
1847 int mTickLabelPadding, mLabelPadding, mPadding;
1848 double mTickLabelRotation;
1849 bool mTicks, mTickLabels, mAutoTicks, mAutoTickLabels, mAutoTickStep, mAutoSubTicks;
1850 bool mRangeReversed;
1851 SelectableParts mSelectable, mSelected;
1852 QFont mSelectedTickLabelFont, mSelectedLabelFont;
1853 QColor mSelectedTickLabelColor, mSelectedLabelColor;
1854 QPen mSelectedBasePen, mSelectedTickPen, mSelectedSubTickPen;
1855 QRect mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox;
1856
1857 // internal or not explicitly exposed properties:
1858 QCPGrid *mGrid;
1859 QVector<double> mSubTickVector;
1860 QChar mExponentialChar, mPositiveSignChar;
1861 int mNumberPrecision;
1862 char mNumberFormatChar;
1863 bool mNumberBeautifulPowers, mNumberMultiplyCross;
1864 Qt::Orientation mOrientation;
1865 int mLowestVisibleTick, mHighestVisibleTick;
1866
1867 // internal setters:
1868 void setAxisType(AxisType type);
1869 void setAxisRect(const QRect &rect);
1870
1871 // introduced methods:
1872 virtual void setupTickVectors();
1873 virtual void generateAutoTicks();
1874 virtual int calculateAutoSubTickCount(double tickStep) const;
1875 virtual int calculateMargin() const;
1876 virtual bool handleAxisSelection(QMouseEvent *event, bool additiveSelection, bool &modified);
1877
1878 // drawing:
1879 virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
1880 virtual void draw(QCPPainter *painter);
1881 virtual void drawTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize);
1882 virtual void getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const;
1883
1884 // basic non virtual helpers:
1885 void visibleTickBounds(int &lowIndex, int &highIndex) const;
1886 double baseLog(double value) const;
1887 double basePow(double value) const;
1888
1889 // helpers to get the right pen/font depending on selection state:
1890 QPen getBasePen() const;
1891 QPen getTickPen() const;
1892 QPen getSubTickPen() const;
1893 QFont getTickLabelFont() const;
1894 QFont getLabelFont() const;
1895 QColor getTickLabelColor() const;
1896 QColor getLabelColor() const;
1897
1898 private:
1899 Q_DISABLE_COPY(QCPAxis)
1900
1901 friend class QCustomPlot;
1902 friend class QCPGrid;
1903 };
1904 Q_DECLARE_OPERATORS_FOR_FLAGS(QCPAxis::SelectableParts)
1905
1906 class QCP_LIB_DECL QCustomPlot : public QWidget
1907 {
1908 Q_OBJECT
1909 /// \cond INCLUDE_QPROPERTIES
1910 Q_PROPERTY(QString title READ title WRITE setTitle)
1911 Q_PROPERTY(QRect axisRect READ axisRect WRITE setAxisRect)
1912 Q_PROPERTY(int marginLeft READ marginLeft WRITE setMarginLeft)
1913 Q_PROPERTY(int marginRight READ marginRight WRITE setMarginRight)
1914 Q_PROPERTY(int marginTop READ marginTop WRITE setMarginTop)
1915 Q_PROPERTY(int marginBottom READ marginBottom WRITE setMarginBottom)
1916 Q_PROPERTY(int autoMargin READ autoMargin WRITE setAutoMargin)
1917 Q_PROPERTY(QColor color READ color WRITE setColor)
1918 Q_PROPERTY(Qt::Orientations rangeDrag READ rangeDrag WRITE setRangeDrag)
1919 Q_PROPERTY(Qt::Orientations rangeZoom READ rangeZoom WRITE setRangeZoom)
1920 /// \endcond
1921 public:
1922 /*!
1923 Defines the mouse interactions possible with QCustomPlot
1924
1925 \c Interactions is a flag of or-combined elements of this enum type.
1926 \see setInteractions, setInteraction
1927 */
1928 enum Interaction { iRangeDrag = 0x001 ///< <tt>0x001</tt> Axis ranges are draggable (see \ref setRangeDrag, \ref setRangeDragAxes)
1929 ,iRangeZoom = 0x002 ///< <tt>0x002</tt> Axis ranges are zoomable with the mouse wheel (see \ref setRangeZoom, \ref setRangeZoomAxes)
1930 ,iMultiSelect = 0x004 ///< <tt>0x004</tt> The user can select multiple objects by holding the modifier set by \ref setMultiSelectModifier while clicking
1931 ,iSelectTitle = 0x008 ///< <tt>0x008</tt> The plot title is selectable
1932 ,iSelectPlottables = 0x010 ///< <tt>0x010</tt> Plottables are selectable
1933 ,iSelectAxes = 0x020 ///< <tt>0x020</tt> Axes are selectable (or parts of them, see QCPAxis::setSelectable)
1934 ,iSelectLegend = 0x040 ///< <tt>0x040</tt> Legends are selectable (or their child items, see QCPLegend::setSelectable)
1935 ,iSelectItems = 0x080 ///< <tt>0x080</tt> Items are selectable (Rectangles, Arrows, Textitems, etc. see \ref QCPAbstractItem)
1936 };
1937 Q_ENUMS(Interaction)
1938 Q_DECLARE_FLAGS(Interactions, Interaction)
1939 /*!
1940 Defines how a layer should be inserted relative to a specified other layer.
1941
1942 \see addLayer, moveLayer
1943 */
1944 enum LayerInsertMode { limBelow ///< Layer is inserted below other layer
1945 ,limAbove ///< Layer is inserted above other layer
1946 };
1947 Q_ENUMS(LayerInsertMode)
1948
1949 explicit QCustomPlot(QWidget *parent = 0);
1950 virtual ~QCustomPlot();
1951
1952 // getters:
1953 QString title() const { return mTitle; }
1954 QFont titleFont() const { return mTitleFont; }
1955 QColor titleColor() const { return mTitleColor; }
1956 QRect axisRect() const { return mAxisRect; }
1957 QRect viewport() const { return mViewport; }
1958 int marginLeft() const { return mMarginLeft; }
1959 int marginRight() const { return mMarginRight; }
1960 int marginTop() const { return mMarginTop; }
1961 int marginBottom() const { return mMarginBottom; }
1962 bool autoMargin() const { return mAutoMargin; }
1963 QColor color() const { return mColor; }
1964 Qt::Orientations rangeDrag() const { return mRangeDrag; }
1965 Qt::Orientations rangeZoom() const { return mRangeZoom; }
1966 QCPAxis *rangeDragAxis(Qt::Orientation orientation);
1967 QCPAxis *rangeZoomAxis(Qt::Orientation orientation);
1968 double rangeZoomFactor(Qt::Orientation orientation);
1969 QCP::AntialiasedElements antialiasedElements() const { return mAntialiasedElements; }
1970 QCP::AntialiasedElements notAntialiasedElements() const { return mNotAntialiasedElements; }
1971 bool autoAddPlottableToLegend() const { return mAutoAddPlottableToLegend; }
1972 QPixmap axisBackground() const { return mAxisBackground; }
1973 bool axisBackgroundScaled() const { return mAxisBackgroundScaled; }
1974 Qt::AspectRatioMode axisBackgroundScaledMode() const { return mAxisBackgroundScaledMode; }
1975 const Interactions interactions() const { return mInteractions; }
1976 int selectionTolerance() const { return mSelectionTolerance; }
1977 QFont selectedTitleFont() const { return mSelectedTitleFont; }
1978 QColor selectedTitleColor() const { return mSelectedTitleColor; }
1979 bool titleSelected() const { return mTitleSelected; }
1980 bool noAntialiasingOnDrag() const { return mNoAntialiasingOnDrag; }
1981 QCP::PlottingHints plottingHints() const { return mPlottingHints; }
1982 Qt::KeyboardModifier multiSelectModifier() const { return mMultiSelectModifier; }
1983
1984 // setters:
1985 void setTitle(const QString &title);
1986 void setTitleFont(const QFont &font);
1987 void setTitleColor(const QColor &color);
1988 void setAxisRect(const QRect &arect);
1989 void setMarginLeft(int margin);
1990 void setMarginRight(int margin);
1991 void setMarginTop(int margin);
1992 void setMarginBottom(int margin);
1993 void setMargin(int left, int right, int top, int bottom);
1994 void setAutoMargin(bool enabled);
1995 void setColor(const QColor &color);
1996 void setRangeDrag(Qt::Orientations orientations);
1997 void setRangeZoom(Qt::Orientations orientations);
1998 void setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical);
1999 void setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical);
2000 void setRangeZoomFactor(double horizontalFactor, double verticalFactor);
2001 void setRangeZoomFactor(double factor);
2002 void setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements);
2003 void setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled=true);
2004 void setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements);
2005 void setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled=true);
2006 void setAutoAddPlottableToLegend(bool on);
2007 void setAxisBackground(const QPixmap &pm);
2008 void setAxisBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode=Qt::KeepAspectRatioByExpanding);
2009 void setAxisBackgroundScaled(bool scaled);
2010 void setAxisBackgroundScaledMode(Qt::AspectRatioMode mode);
2011 void setInteractions(const Interactions &interactions);
2012 void setInteraction(const Interaction &interaction, bool enabled=true);
2013 void setSelectionTolerance(int pixels);
2014 void setSelectedTitleFont(const QFont &font);
2015 void setSelectedTitleColor(const QColor &color);
2016 void setTitleSelected(bool selected);
2017 void setNoAntialiasingOnDrag(bool enabled);
2018 void setPlottingHints(const QCP::PlottingHints &hints);
2019 void setPlottingHint(QCP::PlottingHint hint, bool enabled=true);
2020 void setMultiSelectModifier(Qt::KeyboardModifier modifier);
2021
2022 // non-property methods:
2023 // plottable interface:
2024 QCPAbstractPlottable *plottable(int index);
2025 QCPAbstractPlottable *plottable();
2026 bool addPlottable(QCPAbstractPlottable *plottable);
2027 bool removePlottable(QCPAbstractPlottable *plottable);
2028 bool removePlottable(int index);
2029 int clearPlottables();
2030 int plottableCount() const;
2031 QList<QCPAbstractPlottable*> selectedPlottables() const;
2032 QCPAbstractPlottable *plottableAt(const QPointF &pos, bool onlySelectable=false) const;
2033 bool hasPlottable(QCPAbstractPlottable *plottable) const;
2034
2035 // specialized interface for QCPGraph:
2036 QCPGraph *graph(int index) const;
2037 QCPGraph *graph() const;
2038 QCPGraph *addGraph(QCPAxis *keyAxis=0, QCPAxis *valueAxis=0);
2039 bool removeGraph(QCPGraph *graph);
2040 bool removeGraph(int index);
2041 int clearGraphs();
2042 int graphCount() const;
2043 QList<QCPGraph*> selectedGraphs() const;
2044
2045 // item interface:
2046 QCPAbstractItem *item(int index) const;
2047 QCPAbstractItem *item() const;
2048 bool addItem(QCPAbstractItem* item);
2049 bool removeItem(QCPAbstractItem *item);
2050 bool removeItem(int index);
2051 int clearItems();
2052 int itemCount() const;
2053 QList<QCPAbstractItem*> selectedItems() const;
2054 QCPAbstractItem *itemAt(const QPointF &pos, bool onlySelectable=false) const;
2055
2056 // layer interface:
2057 QCPLayer *layer(const QString &name) const;
2058 QCPLayer *layer(int index) const;
2059 QCPLayer *currentLayer() const;
2060 bool setCurrentLayer(const QString &name);
2061 bool setCurrentLayer(QCPLayer *layer);
2062 int layerCount() const;
2063 bool addLayer(const QString &name, QCPLayer *otherLayer=0, LayerInsertMode insertMode=limAbove);
2064 bool removeLayer(QCPLayer *layer);
2065 bool moveLayer(QCPLayer *layer, QCPLayer *otherLayer, LayerInsertMode insertMode=limAbove);
2066
2067 QList<QCPAxis*> selectedAxes() const;
2068 QList<QCPLegend*> selectedLegends() const;
2069 void setupFullAxesBox();
2070 bool savePdf(const QString &fileName, bool noCosmeticPen=false, int width=0, int height=0);
2071 bool savePng(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1);
2072 bool saveJpg(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1);
2073 bool saveBmp(const QString &fileName, int width=0, int height=0, double scale=1.0);
2074 bool saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality=-1);
2075
2076 QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
2077 QCPLegend *legend;
2078
2079 public slots:
2080 void deselectAll();
2081 void replot();
2082 void rescaleAxes();
2083
2084 signals:
2085 void mouseDoubleClick(QMouseEvent *event);
2086 void mousePress(QMouseEvent *event);
2087 void mouseMove(QMouseEvent *event);
2088 void mouseRelease(QMouseEvent *event);
2089 void mouseWheel(QWheelEvent *event);
2090
2091 void plottableClick(QCPAbstractPlottable *plottable, QMouseEvent *event);
2092 void plottableDoubleClick(QCPAbstractPlottable *plottable, QMouseEvent *event);
2093 void itemClick(QCPAbstractItem *item, QMouseEvent *event);
2094 void itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event);
2095 void axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event);
2096 void axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event);
2097 void legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event);
2098 void legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event);
2099 void titleClick(QMouseEvent *event);
2100 void titleDoubleClick(QMouseEvent *event);
2101
2102 void selectionChangedByUser();
2103 void beforeReplot();
2104 void afterReplot();
2105
2106 protected:
2107 QString mTitle;
2108 QFont mTitleFont, mSelectedTitleFont;
2109 QColor mTitleColor, mSelectedTitleColor;
2110 QRect mViewport;
2111 QRect mAxisRect;
2112 int mMarginLeft, mMarginRight, mMarginTop, mMarginBottom;
2113 bool mAutoMargin, mAutoAddPlottableToLegend;
2114 QColor mColor;
2115 QList<QCPAbstractPlottable*> mPlottables;
2116 QList<QCPGraph*> mGraphs; // extra list of items also in mPlottables that are of type QCPGraph
2117 QList<QCPAbstractItem*> mItems;
2118 QList<QCPLayer*> mLayers;
2119 Qt::Orientations mRangeDrag, mRangeZoom;
2120 QCPAxis *mRangeDragHorzAxis, *mRangeDragVertAxis, *mRangeZoomHorzAxis, *mRangeZoomVertAxis;
2121 double mRangeZoomFactorHorz, mRangeZoomFactorVert;
2122 bool mDragging;
2123 QCP::AntialiasedElements mAntialiasedElements, mNotAntialiasedElements;
2124 QPixmap mAxisBackground;
2125 bool mAxisBackgroundScaled;
2126 Qt::AspectRatioMode mAxisBackgroundScaledMode;
2127 Interactions mInteractions;
2128 int mSelectionTolerance;
2129 bool mTitleSelected;
2130 QRect mTitleBoundingBox;
2131 bool mNoAntialiasingOnDrag;
2132 // not explicitly exposed properties:
2133 QPixmap mPaintBuffer;
2134 QPoint mDragStart;
2135 QCPRange mDragStartHorzRange, mDragStartVertRange;
2136 QPixmap mScaledAxisBackground;
2137 bool mReplotting;
2138 QCP::AntialiasedElements mAADragBackup, mNotAADragBackup;
2139 QCPLayer *mCurrentLayer;
2140 QCP::PlottingHints mPlottingHints;
2141 Qt::KeyboardModifier mMultiSelectModifier;
2142
2143 // reimplemented methods:
2144 virtual QSize minimumSizeHint() const;
2145 virtual void paintEvent(QPaintEvent *event);
2146 virtual void resizeEvent(QResizeEvent *event);
2147 virtual void mouseDoubleClickEvent(QMouseEvent *event);
2148 virtual void mousePressEvent(QMouseEvent *event);
2149 virtual void mouseMoveEvent(QMouseEvent *event);
2150 virtual void mouseReleaseEvent(QMouseEvent *event);
2151 virtual void wheelEvent(QWheelEvent *event);
2152 // event helpers:
2153 virtual bool handlePlottableSelection(QMouseEvent *event, bool additiveSelection, bool &modified);
2154 virtual bool handleItemSelection(QMouseEvent *event, bool additiveSelection, bool &modified);
2155 virtual bool handleAxisSelection(QMouseEvent *event, bool additiveSelection, bool &modified);
2156 virtual bool handleTitleSelection(QMouseEvent *event, bool additiveSelection, bool &modified);
2157
2158 // introduced methods:
2159 virtual void draw(QCPPainter *painter);
2160 virtual void drawAxisBackground(QCPPainter *painter);
2161
2162 // helpers:
2163 void updateAxisRect();
2164 bool selectTestTitle(const QPointF &pos) const;
2165 friend class QCPLegend;
2166 friend class QCPAxis;
2167 friend class QCPLayer;
2168 };
2169 Q_DECLARE_OPERATORS_FOR_FLAGS(QCustomPlot::Interactions)
2170
2171 #endif // QCUSTOMPLOT_H
@@ -0,0 +1,406
1 #include "lppmonplot.h"
2
3
4
5 LppMonPlot::LppMonPlot(QWidget *parent) :
6 QWidget(parent)
7 {
8 this->m_plot = new QCustomPlot(this);
9 this->m_plot->setInteractions(QCustomPlot::iRangeDrag | QCustomPlot::iSelectAxes |
10 QCustomPlot::iSelectLegend | QCustomPlot::iSelectPlottables | QCustomPlot::iSelectTitle);
11 this->m_plot->setRangeDrag(Qt::Horizontal|Qt::Vertical);
12 this->m_plot->setRangeZoom(Qt::Horizontal|Qt::Vertical);
13 this->m_mainlayout = new QGridLayout(this);
14 this->setLayout(this->m_mainlayout);
15 this->m_mainlayout->addWidget(this->m_plot);
16 this->setMinimumSize(400,300);
17 this->setFocusPolicy(Qt::WheelFocus);
18 this->m_plot->setAttribute(Qt::WA_TransparentForMouseEvents);
19 this->ctrl_hold = false;
20 this->shift_hold = false;
21 this->mouse_hold = false;
22 this->show();
23
24 }
25
26 void LppMonPlot::show()
27 {
28 QWidget::show();
29 }
30
31 void LppMonPlot::setTitle(QString title)
32 {
33 this->m_plot->setTitle(title);
34 this->repaint();
35 }
36
37 void LppMonPlot::setXaxisLabel(QString label)
38 {
39 this->m_plot->xAxis->setLabel(label);
40 this->repaint();
41 }
42
43 void LppMonPlot::setYaxisLabel(QString label)
44 {
45 this->m_plot->yAxis->setLabel(label);
46 this->repaint();
47 }
48
49 void LppMonPlot::setXaxisRange(double lower, double upper)
50 {
51 this->m_plot->xAxis->setRange(lower,upper);
52 }
53
54 void LppMonPlot::setYaxisRange(double lower, double upper)
55 {
56 this->m_plot->yAxis->setRange(lower,upper);
57 }
58
59
60 void LppMonPlot::rescaleAxis()
61 {
62 this->m_plot->rescaleAxes();
63 this->m_plot->replot();
64 }
65
66 void LppMonPlot::setLegendFont(QFont font)
67 {
68 this->m_plot->legend->setFont(font);
69 this->repaint();
70 }
71
72 void LppMonPlot::setLegendSelectedFont(QFont font)
73 {
74 this->m_plot->legend->setSelectedFont(font);
75 this->repaint();
76 }
77
78 int LppMonPlot::addGraph()
79 {
80 this->m_plot->addGraph();
81 return this->m_plot->graphCount() -1;
82 }
83
84
85 void LppMonPlot::setGraphName(int graphIndex,QString name)
86 {
87 if(graphIndex<this->m_plot->graphCount())
88 {
89 this->m_plot->graph(graphIndex)->setName(name);
90 }
91 }
92
93
94 void LppMonPlot::setGraphData(int graphIndex, QList<QVariant> x, QList<QVariant> y)
95 {
96 if((graphIndex<this->m_plot->graphCount()) && (x.count()==y.count()))// && (x.at(0).type()==QVariant::Double))
97 {
98 QVector<double> _x(x.count()), _y(y.count());
99 for(int i=0;i<x.count();i++)
100 {
101 /*_x[i] = x.at(i).value<double>();
102 _y[i] = y.at(i).value<double>();*/
103 _x[i] = x.at(i).toDouble();
104 _y[i] = y.at(i).toDouble();
105 }
106 this->m_plot->graph(graphIndex)->setData(_x,_y);
107 }
108 this->m_plot->replot();
109 }
110
111 void LppMonPlot::addGraphData(int graphIndex, QList<QVariant> x, QList<QVariant> y)
112 {
113 if((graphIndex<this->m_plot->graphCount()) && (x.count()==y.count()))// && (x.at(0).type()==QVariant::Double))
114 {
115 QVector<double> _x(x.count()), _y(y.count());
116 for(int i=0;i<x.count();i++)
117 {
118 /*_x[i] = x.at(i).value<double>();
119 _y[i] = y.at(i).value<double>();*/
120 _x[i] = x.at(i).toDouble();
121 _y[i] = y.at(i).toDouble();
122 }
123 this->m_plot->graph(graphIndex)->addData(_x,_y);
124 }
125 this->m_plot->replot();
126 }
127
128 void LppMonPlot::addGraphData(int graphIndex, QVariant x, QVariant y)
129 {
130 if(graphIndex<this->m_plot->graphCount())// && (x.at(0).type()==QVariant::Double))
131 {
132 this->m_plot->graph(graphIndex)->addData(x.toDouble(),y.toDouble());
133 }
134 this->m_plot->replot();
135 }
136
137 void LppMonPlot::setGraphPen(int graphIndex,QPen pen)
138 {
139 if(graphIndex<this->m_plot->graphCount())
140 {
141 this->m_plot->graph(graphIndex)->setPen(pen);
142 }
143 }
144
145 QPen LppMonPlot::getGraphPen(int graphIndex)
146 {
147 if(graphIndex<this->m_plot->graphCount())
148 {
149 return this->m_plot->graph(graphIndex)->pen();
150 }
151 }
152
153
154
155 void LppMonPlot::setGraphLineStyle(int graphIndex,QString lineStyle)
156 {
157 if(graphIndex<this->m_plot->graphCount())
158 {
159 if(!lineStyle.compare("none"))
160 {
161 this->m_plot->graph(graphIndex)->setLineStyle(QCPGraph::lsNone);
162 return;
163 }
164 if(!lineStyle.compare("line"))
165 {
166 this->m_plot->graph(graphIndex)->setLineStyle(QCPGraph::lsLine);
167 return;
168 }
169 if(!lineStyle.compare("stepleft"))
170 {
171 this->m_plot->graph(graphIndex)->setLineStyle(QCPGraph::lsStepLeft);
172 return;
173 }
174 if(!lineStyle.compare("stepright"))
175 {
176 this->m_plot->graph(graphIndex)->setLineStyle(QCPGraph::lsStepRight);
177 return;
178 }
179 if(!lineStyle.compare("stepcenter"))
180 {
181 this->m_plot->graph(graphIndex)->setLineStyle(QCPGraph::lsStepCenter);
182 return;
183 }
184 if(!lineStyle.compare("impulse"))
185 {
186 this->m_plot->graph(graphIndex)->setLineStyle(QCPGraph::lsImpulse);
187 return;
188 }
189
190
191 }
192 }
193
194 void LppMonPlot::setGraphScatterStyle(int graphIndex,QString scatterStyle)
195 {
196 if(graphIndex<this->m_plot->graphCount())
197 {
198 if(!scatterStyle.compare("none"))
199 {
200 this->m_plot->graph(graphIndex)->setScatterStyle(QCP::ssNone);
201 return;
202 }
203 if(!scatterStyle.compare("dot"))
204 {
205 this->m_plot->graph(graphIndex)->setScatterStyle(QCP::ssDot);
206 return;
207 }
208 if(!scatterStyle.compare("cross"))
209 {
210 this->m_plot->graph(graphIndex)->setScatterStyle(QCP::ssCross);
211 return;
212 }
213 if(!scatterStyle.compare("plus"))
214 {
215 this->m_plot->graph(graphIndex)->setScatterStyle(QCP::ssPlus);
216 return;
217 }
218 if(!scatterStyle.compare("circle"))
219 {
220 this->m_plot->graph(graphIndex)->setScatterStyle(QCP::ssCircle);
221 return;
222 }
223 if(!scatterStyle.compare("disc"))
224 {
225 this->m_plot->graph(graphIndex)->setScatterStyle(QCP::ssDisc);
226 return;
227 }
228 if(!scatterStyle.compare("square"))
229 {
230 this->m_plot->graph(graphIndex)->setScatterStyle(QCP::ssSquare);
231 return;
232 }
233 if(!scatterStyle.compare("diamond"))
234 {
235 this->m_plot->graph(graphIndex)->setScatterStyle(QCP::ssDiamond);
236 return;
237 }
238 if(!scatterStyle.compare("star"))
239 {
240 this->m_plot->graph(graphIndex)->setScatterStyle(QCP::ssStar);
241 return;
242 }
243 if(!scatterStyle.compare("triangle"))
244 {
245 this->m_plot->graph(graphIndex)->setScatterStyle(QCP::ssTriangle);
246 return;
247 }
248 if(!scatterStyle.compare("invertedtriangle"))
249 {
250 this->m_plot->graph(graphIndex)->setScatterStyle(QCP::ssTriangleInverted);
251 return;
252 }
253 if(!scatterStyle.compare("crosssquare"))
254 {
255 this->m_plot->graph(graphIndex)->setScatterStyle(QCP::ssCrossSquare);
256 return;
257 }
258 if(!scatterStyle.compare("plussquare"))
259 {
260 this->m_plot->graph(graphIndex)->setScatterStyle(QCP::ssPlusSquare);
261 return;
262 }
263 if(!scatterStyle.compare("crosscircle"))
264 {
265 this->m_plot->graph(graphIndex)->setScatterStyle(QCP::ssCrossCircle);
266 return;
267 }
268 if(!scatterStyle.compare("pluscircle"))
269 {
270 this->m_plot->graph(graphIndex)->setScatterStyle(QCP::ssPlusCircle);
271 return;
272 }
273 if(!scatterStyle.compare("peace"))
274 {
275 this->m_plot->graph(graphIndex)->setScatterStyle(QCP::ssPeace);
276 return;
277 }
278
279 }
280 }
281
282
283
284
285
286 void LppMonPlot::keyPressEvent(QKeyEvent * event)
287 {
288 switch(event->key())
289 {
290 case Qt::Key_Control:
291 this->ctrl_hold = true;
292 break;
293 case Qt::Key_Shift:
294 this->shift_hold = true;
295 break;
296 case Qt::Key_M:
297 this->rescaleAxis();
298 break;
299 default:
300 QWidget::keyPressEvent(event);
301 break;
302 }
303 }
304
305 void LppMonPlot::keyReleaseEvent(QKeyEvent * event)
306 {
307 switch(event->key())
308 {
309 case Qt::Key_Control:
310 event->accept();
311 this->ctrl_hold = false;
312 break;
313 case Qt::Key_Shift:
314 event->accept();
315 this->shift_hold = false;
316 break;
317 default:
318 QWidget::keyReleaseEvent(event);
319 break;
320 }
321 }
322
323 void LppMonPlot::wheelEvent(QWheelEvent * event)
324 {
325 double factor;
326 double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
327 if(ctrl_hold)
328 {
329 if (event->orientation()==Qt::Vertical)//mRangeZoom.testFlag(Qt::Vertical))
330 {
331 factor = pow(this->m_plot->rangeZoomFactor(Qt::Vertical), wheelSteps);
332 QCPAxis* axis = this->m_plot->rangeZoomAxis(Qt::Vertical);
333 axis->scaleRange(factor, axis->pixelToCoord(event->pos().y()));
334 }
335 this->m_plot->replot();
336 QWidget::wheelEvent(event);
337 return;
338 }
339 if(shift_hold)
340 {
341 if (event->orientation()==Qt::Vertical)//mRangeZoom.testFlag(Qt::Vertical))
342 {
343 factor = pow(this->m_plot->rangeZoomFactor(Qt::Horizontal), wheelSteps);
344 QCPAxis* axis = this->m_plot->rangeZoomAxis(Qt::Horizontal);
345 axis->scaleRange(factor, axis->pixelToCoord(event->pos().x()));
346 }
347 this->m_plot->replot();
348 QWidget::wheelEvent(event);
349 return;
350 }
351 QCPAxis* Haxis = this->m_plot->rangeDragAxis(Qt::Horizontal);
352 double rg = (Haxis->range().upper - Haxis->range().lower)*(wheelSteps/10);
353 Haxis->setRange(Haxis->range().lower+(rg), Haxis->range().upper+(rg));
354 this->m_plot->replot();
355 QWidget::wheelEvent(event);
356 }
357
358
359
360
361 void LppMonPlot::mousePressEvent(QMouseEvent *event)
362 {
363 if(event->button()==Qt::LeftButton)
364 {
365 mDragStart = event->pos();
366 this->mouse_hold = true;
367 DragStartHorzRange = this->m_plot->rangeDragAxis(Qt::Horizontal)->range();
368 DragStartVertRange = this->m_plot->rangeDragAxis(Qt::Vertical)->range();
369 }
370 QWidget::mousePressEvent(event);
371 }
372
373 void LppMonPlot::mouseReleaseEvent(QMouseEvent *event)
374 {
375 if(event->button()==Qt::LeftButton)
376 {
377 this->mouse_hold = false;
378 }
379 QWidget::mouseReleaseEvent(event);
380 }
381
382 void LppMonPlot::mouseMoveEvent(QMouseEvent *event)
383 {
384 if(mouse_hold)
385 {
386 QCPAxis* Haxis = this->m_plot->rangeDragAxis(Qt::Horizontal);
387 QCPAxis* Vaxis = this->m_plot->rangeDragAxis(Qt::Vertical);
388 double diff = Haxis->pixelToCoord(mDragStart.x()) - Haxis->pixelToCoord(event->pos().x());
389 Haxis->setRange(DragStartHorzRange.lower+diff, DragStartHorzRange.upper+diff);
390 diff = Vaxis->pixelToCoord(mDragStart.y()) - Vaxis->pixelToCoord(event->pos().y());
391 Vaxis->setRange(DragStartVertRange.lower+diff, DragStartVertRange.upper+diff);
392 this->m_plot->replot();
393 }
394 QWidget::mouseMoveEvent(event);
395 }
396
397
398
399
400
401
402
403
404
405
406
@@ -0,0 +1,55
1 #ifndef LPPMONPLOT_H
2 #define LPPMONPLOT_H
3
4 #include <QWidget>
5 #include <QGridLayout>
6 #include <qcustomplot.h>
7
8 class LppMonPlot : public QWidget
9 {
10 Q_OBJECT
11 public:
12 explicit LppMonPlot(QWidget *parent = 0);
13 void setTitle(QString title);
14 void setXaxisLabel(QString label);
15 void setXaxisRange(double lower, double upper);
16 void setYaxisLabel(QString label);
17 void setYaxisRange(double lower, double upper);
18 void rescaleAxis();
19 void setLegendFont(QFont font);
20 void setLegendSelectedFont(QFont font);
21 int addGraph();
22 void setGraphName(int graphIndex,QString name);
23 void setGraphData(int graphIndex, QList<QVariant> x, QList<QVariant> y);
24 void addGraphData(int graphIndex, QList<QVariant> x, QList<QVariant> y);
25 void addGraphData(int graphIndex, QVariant x, QVariant y);
26 void setGraphPen(int graphIndex,QPen pen);
27 QPen getGraphPen(int graphIndex);
28 void setGraphLineStyle(int graphIndex,QString lineStyle);
29 void setGraphScatterStyle(int graphIndex,QString scatterStyle);
30 void show();
31
32 signals:
33
34 public slots:
35
36 protected:
37 void keyPressEvent(QKeyEvent *);
38 void keyReleaseEvent(QKeyEvent *);
39 void wheelEvent(QWheelEvent *);
40 void mousePressEvent(QMouseEvent *);
41 void mouseMoveEvent(QMouseEvent *);
42 void mouseReleaseEvent(QMouseEvent *);
43
44 private:
45 QCustomPlot* m_plot;
46 QGridLayout* m_mainlayout;
47 bool ctrl_hold;
48 bool shift_hold;
49 bool mouse_hold;
50 QCPRange DragStartHorzRange;
51 QCPRange DragStartVertRange;
52 QPoint mDragStart;
53 };
54
55 #endif // LPPMONPLOT_H
@@ -1,6 +1,6
1 <?xml version="1.0" encoding="UTF-8"?>
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE QtCreatorProject>
2 <!DOCTYPE QtCreatorProject>
3 <!-- Written by Qt Creator 2.4.1, 2013-07-04T13:25:30. -->
3 <!-- Written by Qt Creator 2.4.1, 2013-07-04T16:03:07. -->
4 <qtcreator>
4 <qtcreator>
5 <data>
5 <data>
6 <variable>ProjectExplorer.Project.ActiveTarget</variable>
6 <variable>ProjectExplorer.Project.ActiveTarget</variable>
@@ -1,6 +1,6
1 <?xml version="1.0" encoding="UTF-8"?>
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE QtCreatorProject>
2 <!DOCTYPE QtCreatorProject>
3 <!-- Written by Qt Creator 2.4.1, 2013-07-04T14:46:51. -->
3 <!-- Written by Qt Creator 2.4.1, 2013-07-05T07:56:16. -->
4 <qtcreator>
4 <qtcreator>
5 <data>
5 <data>
6 <variable>ProjectExplorer.Project.ActiveTarget</variable>
6 <variable>ProjectExplorer.Project.ActiveTarget</variable>
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -1,9 +1,9
1 #############################################################################
1 #############################################################################
2 # Makefile for building: libwfdisplay.so.1.0.0
2 # Makefile for building: libwfdisplay.so.1.0.0
3 # Generated by qmake (2.01a) (Qt 4.8.4) on: Thu Jul 4 14:33:47 2013
3 # Generated by qmake (2.01a) (Qt 4.8.4) on: Fri Jul 5 07:42:37 2013
4 # Project: wfdisplay.pro
4 # Project: wfdisplay.pro
5 # Template: lib
5 # Template: lib
6 # Command: /bin/qmake-qt4 -o Makefile wfdisplay.pro
6 # Command: /usr/bin/qmake-qt4 -o Makefile wfdisplay.pro
7 #############################################################################
7 #############################################################################
8
8
9 ####### Compiler, tools and options
9 ####### Compiler, tools and options
@@ -13,13 +13,13 CXX = g++
13 DEFINES = -DWFDISPLAY_LIBRARY -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED
13 DEFINES = -DWFDISPLAY_LIBRARY -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED
14 CFLAGS = -pipe -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -O2 -Wall -W -D_REENTRANT -fPIC $(DEFINES)
14 CFLAGS = -pipe -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -O2 -Wall -W -D_REENTRANT -fPIC $(DEFINES)
15 CXXFLAGS = -pipe -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -O2 -Wall -W -D_REENTRANT -fPIC $(DEFINES)
15 CXXFLAGS = -pipe -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -O2 -Wall -W -D_REENTRANT -fPIC $(DEFINES)
16 INCPATH = -I/usr/lib64/qt4/mkspecs/linux-g++ -I. -I/usr/include/QtCore -I/usr/include/QtGui -I/usr/include -I. -I/usr/include/lppmon/common -I.
16 INCPATH = -I/usr/lib64/qt4/mkspecs/linux-g++ -I. -I/usr/include/QtCore -I/usr/include/QtGui -I/usr/include -I. -Ilppmonplot -IQCustomPlot -I.
17 LINK = g++
17 LINK = g++
18 LFLAGS = -Wl,-O1 -Wl,-z,relro -shared -Wl,-soname,libwfdisplay.so.1
18 LFLAGS = -Wl,-O1 -Wl,-z,relro -shared -Wl,-soname,libwfdisplay.so.1
19 LIBS = $(SUBLIBS) -L/usr/lib64 -llppmoncommon -lQtGui -lQtCore -lpthread
19 LIBS = $(SUBLIBS) -L/usr/lib64 -lQtGui -lQtCore -lpthread
20 AR = ar cqs
20 AR = ar cqs
21 RANLIB =
21 RANLIB =
22 QMAKE = /bin/qmake-qt4
22 QMAKE = /usr/bin/qmake-qt4
23 TAR = tar -cf
23 TAR = tar -cf
24 COMPRESS = gzip -9f
24 COMPRESS = gzip -9f
25 COPY = cp -f
25 COPY = cp -f
@@ -45,15 +45,23 OBJECTS_DIR = ./
45
45
46 SOURCES = wfdisplay.cpp \
46 SOURCES = wfdisplay.cpp \
47 wfplot.cpp \
47 wfplot.cpp \
48 wfpage.cpp moc_wfdisplay.cpp \
48 wfpage.cpp \
49 lppmonplot/lppmonplot.cpp \
50 QCustomPlot/qcustomplot.cpp moc_wfdisplay.cpp \
49 moc_wfplot.cpp \
51 moc_wfplot.cpp \
50 moc_wfpage.cpp
52 moc_wfpage.cpp \
53 moc_lppmonplot.cpp \
54 moc_qcustomplot.cpp
51 OBJECTS = wfdisplay.o \
55 OBJECTS = wfdisplay.o \
52 wfplot.o \
56 wfplot.o \
53 wfpage.o \
57 wfpage.o \
58 lppmonplot.o \
59 qcustomplot.o \
54 moc_wfdisplay.o \
60 moc_wfdisplay.o \
55 moc_wfplot.o \
61 moc_wfplot.o \
56 moc_wfpage.o
62 moc_wfpage.o \
63 moc_lppmonplot.o \
64 moc_qcustomplot.o
57 DIST = /usr/lib64/qt4/mkspecs/common/unix.conf \
65 DIST = /usr/lib64/qt4/mkspecs/common/unix.conf \
58 /usr/lib64/qt4/mkspecs/common/linux.conf \
66 /usr/lib64/qt4/mkspecs/common/linux.conf \
59 /usr/lib64/qt4/mkspecs/common/gcc-base.conf \
67 /usr/lib64/qt4/mkspecs/common/gcc-base.conf \
@@ -185,7 +193,7 qmake: FORCE
185
193
186 dist:
194 dist:
187 @$(CHK_DIR_EXISTS) .tmp/wfdisplay1.0.0 || $(MKDIR) .tmp/wfdisplay1.0.0
195 @$(CHK_DIR_EXISTS) .tmp/wfdisplay1.0.0 || $(MKDIR) .tmp/wfdisplay1.0.0
188 $(COPY_FILE) --parents $(SOURCES) $(DIST) .tmp/wfdisplay1.0.0/ && $(COPY_FILE) --parents wfdisplay.h wfdisplay_global.h wfplot.h wfpage.h wfdisplay_params.h .tmp/wfdisplay1.0.0/ && $(COPY_FILE) --parents wfdisplay.cpp wfplot.cpp wfpage.cpp .tmp/wfdisplay1.0.0/ && (cd `dirname .tmp/wfdisplay1.0.0` && $(TAR) wfdisplay1.0.0.tar wfdisplay1.0.0 && $(COMPRESS) wfdisplay1.0.0.tar) && $(MOVE) `dirname .tmp/wfdisplay1.0.0`/wfdisplay1.0.0.tar.gz . && $(DEL_FILE) -r .tmp/wfdisplay1.0.0
196 $(COPY_FILE) --parents $(SOURCES) $(DIST) .tmp/wfdisplay1.0.0/ && $(COPY_FILE) --parents wfdisplay.h wfdisplay_global.h wfplot.h wfpage.h wfdisplay_params.h lppmonplot/lppmonplot.h QCustomPlot/qcustomplot.h .tmp/wfdisplay1.0.0/ && $(COPY_FILE) --parents wfdisplay.cpp wfplot.cpp wfpage.cpp lppmonplot/lppmonplot.cpp QCustomPlot/qcustomplot.cpp .tmp/wfdisplay1.0.0/ && (cd `dirname .tmp/wfdisplay1.0.0` && $(TAR) wfdisplay1.0.0.tar wfdisplay1.0.0 && $(COMPRESS) wfdisplay1.0.0.tar) && $(MOVE) `dirname .tmp/wfdisplay1.0.0`/wfdisplay1.0.0.tar.gz . && $(DEL_FILE) -r .tmp/wfdisplay1.0.0
189
197
190
198
191 clean:compiler_clean
199 clean:compiler_clean
@@ -207,9 +215,9 mocclean: compiler_moc_header_clean comp
207
215
208 mocables: compiler_moc_header_make_all compiler_moc_source_make_all
216 mocables: compiler_moc_header_make_all compiler_moc_source_make_all
209
217
210 compiler_moc_header_make_all: moc_wfdisplay.cpp moc_wfplot.cpp moc_wfpage.cpp
218 compiler_moc_header_make_all: moc_wfdisplay.cpp moc_wfplot.cpp moc_wfpage.cpp moc_lppmonplot.cpp moc_qcustomplot.cpp
211 compiler_moc_header_clean:
219 compiler_moc_header_clean:
212 -$(DEL_FILE) moc_wfdisplay.cpp moc_wfplot.cpp moc_wfpage.cpp
220 -$(DEL_FILE) moc_wfdisplay.cpp moc_wfplot.cpp moc_wfpage.cpp moc_lppmonplot.cpp moc_qcustomplot.cpp
213 moc_wfdisplay.cpp: wfdisplay_global.h \
221 moc_wfdisplay.cpp: wfdisplay_global.h \
214 wfpage.h \
222 wfpage.h \
215 wfplot.h \
223 wfplot.h \
@@ -226,6 +234,12 moc_wfpage.cpp: wfdisplay_global.h \
226 wfpage.h
234 wfpage.h
227 /usr/lib64/qt4/bin/moc $(DEFINES) $(INCPATH) wfpage.h -o moc_wfpage.cpp
235 /usr/lib64/qt4/bin/moc $(DEFINES) $(INCPATH) wfpage.h -o moc_wfpage.cpp
228
236
237 moc_lppmonplot.cpp: lppmonplot/lppmonplot.h
238 /usr/lib64/qt4/bin/moc $(DEFINES) $(INCPATH) lppmonplot/lppmonplot.h -o moc_lppmonplot.cpp
239
240 moc_qcustomplot.cpp: QCustomPlot/qcustomplot.h
241 /usr/lib64/qt4/bin/moc $(DEFINES) $(INCPATH) QCustomPlot/qcustomplot.h -o moc_qcustomplot.cpp
242
229 compiler_rcc_make_all:
243 compiler_rcc_make_all:
230 compiler_rcc_clean:
244 compiler_rcc_clean:
231 compiler_image_collection_make_all: qmake_image_collection.cpp
245 compiler_image_collection_make_all: qmake_image_collection.cpp
@@ -262,6 +276,12 wfpage.o: wfpage.cpp wfpage.h \
262 wfdisplay_params.h
276 wfdisplay_params.h
263 $(CXX) -c $(CXXFLAGS) $(INCPATH) -o wfpage.o wfpage.cpp
277 $(CXX) -c $(CXXFLAGS) $(INCPATH) -o wfpage.o wfpage.cpp
264
278
279 lppmonplot.o: lppmonplot/lppmonplot.cpp lppmonplot/lppmonplot.h
280 $(CXX) -c $(CXXFLAGS) $(INCPATH) -o lppmonplot.o lppmonplot/lppmonplot.cpp
281
282 qcustomplot.o: QCustomPlot/qcustomplot.cpp QCustomPlot/qcustomplot.h
283 $(CXX) -c $(CXXFLAGS) $(INCPATH) -o qcustomplot.o QCustomPlot/qcustomplot.cpp
284
265 moc_wfdisplay.o: moc_wfdisplay.cpp
285 moc_wfdisplay.o: moc_wfdisplay.cpp
266 $(CXX) -c $(CXXFLAGS) $(INCPATH) -o moc_wfdisplay.o moc_wfdisplay.cpp
286 $(CXX) -c $(CXXFLAGS) $(INCPATH) -o moc_wfdisplay.o moc_wfdisplay.cpp
267
287
@@ -271,6 +291,12 moc_wfplot.o: moc_wfplot.cpp
271 moc_wfpage.o: moc_wfpage.cpp
291 moc_wfpage.o: moc_wfpage.cpp
272 $(CXX) -c $(CXXFLAGS) $(INCPATH) -o moc_wfpage.o moc_wfpage.cpp
292 $(CXX) -c $(CXXFLAGS) $(INCPATH) -o moc_wfpage.o moc_wfpage.cpp
273
293
294 moc_lppmonplot.o: moc_lppmonplot.cpp
295 $(CXX) -c $(CXXFLAGS) $(INCPATH) -o moc_lppmonplot.o moc_lppmonplot.cpp
296
297 moc_qcustomplot.o: moc_qcustomplot.cpp
298 $(CXX) -c $(CXXFLAGS) $(INCPATH) -o moc_qcustomplot.o moc_qcustomplot.cpp
299
274 ####### Install
300 ####### Install
275
301
276 install_header: first FORCE
302 install_header: first FORCE
@@ -9,22 +9,28 TEMPLATE = lib
9
9
10 INCLUDEPATH += \
10 INCLUDEPATH += \
11 $${PWD} \
11 $${PWD} \
12 $$[QT_INSTALL_HEADERS]/lppmon/common
12 ./lppmonplot \
13 ./QCustomPlot
14 # $$[QT_INSTALL_HEADERS]/lppmon/common
13
15
14 LIBS += -llppmoncommon
16 #LIBS += -llppmoncommon
15
17
16 DEFINES += WFDISPLAY_LIBRARY
18 DEFINES += WFDISPLAY_LIBRARY
17
19
18 SOURCES += wfdisplay.cpp \
20 SOURCES += wfdisplay.cpp \
19 wfplot.cpp \
21 wfplot.cpp \
20 wfpage.cpp
22 wfpage.cpp \
23 lppmonplot/lppmonplot.cpp \
24 QCustomPlot/qcustomplot.cpp
21
25
22
26
23 HEADERS += wfdisplay.h\
27 HEADERS += wfdisplay.h\
24 wfdisplay_global.h \
28 wfdisplay_global.h \
25 wfplot.h \
29 wfplot.h \
26 wfpage.h \
30 wfpage.h \
27 wfdisplay_params.h
31 wfdisplay_params.h \
32 lppmonplot/lppmonplot.h \
33 QCustomPlot/qcustomplot.h
28
34
29
35
30 header.path = $$[QT_INSTALL_HEADERS]/lppmon/wfdisplay
36 header.path = $$[QT_INSTALL_HEADERS]/lppmon/wfdisplay
@@ -1,6 +1,6
1 <?xml version="1.0" encoding="UTF-8"?>
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE QtCreatorProject>
2 <!DOCTYPE QtCreatorProject>
3 <!-- Written by Qt Creator 2.4.1, 2013-07-04T14:46:51. -->
3 <!-- Written by Qt Creator 2.4.1, 2013-07-05T07:41:04. -->
4 <qtcreator>
4 <qtcreator>
5 <data>
5 <data>
6 <variable>ProjectExplorer.Project.ActiveTarget</variable>
6 <variable>ProjectExplorer.Project.ActiveTarget</variable>
General Comments 0
You need to be logged in to leave comments. Login now