socnetv-2.2/000755 000765 000024 00000000000 13040735147 014031 5ustar00dimitrisstaff000000 000000 socnetv-2.2/AUTHORS000644 000765 000024 00000000530 13040701535 015071 0ustar00dimitrisstaff000000 000000 Developer: Dimitris Kalamaras See my blog at: http://dimitris.apeiro.gr Translators: - Greek: None - German: Daniel Pinto dos Santos Debian packagers: - Serafeim Zanikolas (>0.43) - Alejandro Garrido Mota (<0.44) Gentoo packager: - Markos Chandras Arch packager: - Tom Tryfonidis socnetv-2.2/changelog.gz000755 000765 000024 00000044474 13040733777 016351 0ustar00dimitrisstaff000000 000000 Xchangelog}[zؕF(cBRR?Y]-[%HB$ $#8C8c8g=u{˕<,uQt_8%SQ~~Ie_2#nћЭYOlk뗤"{Q?kLQuQL<'g(Yd{?Z)zHzYҫ?IY:U_d˪N4Fy:?]w"MiּxIPhOY112`hJ)sŢ(d͓zVL֒%,Ϳ$̒.c:Z?c%8q]ϼ&ZbQe1j,UT&tU:&iU4Le,+hLM~dkkIU,+Yr`8uF?e0VRnweR-:*xt]YoMXj<&?8"Ŵ6.yt ׃6]$ә]$yM֫s˅ߓ,htZA Dۥj\ M4$yHsX#gʜưeIu5JZQoa۸L^Sg+Z yH qccLb-\)(<$S LRo,7mQxI􍈬[v&$+!܌HX1,np!um$ C ,=1xf'ŋ<ÒQ̚pN8:ON^^Ny/C~NC(*JAi쏗i\9A4dҀz^JXJDW@B_?U]ȿK 9]%rJ1lV* 2yI`hgi3m؟> m.Dy@`bvaؔ.Bn l,g ?L3d@IoI2I) ܬpĢu2߆T& AT?d+C}jHRHӜGO(AJ Kū7AW%Ѭ1]h4"b}Rp 9ٸDA*I\'H2crA<&%PZٵݩd ͭCHAȹ,+hWϤ uVXƥi1膴э 9ASC\_MRp1m @*,X[-FX,Vj S$ߙyQQ\M1]\z](.̒Z~,Nchr:Ľ1&,:.(qΜ=,P<%S٨*+ 89ݍ.CdmU$cF2u%VI:MkHM`y|-GPG28IT8{mOdq1oDN=q_Q]&J!`o&k2;Z.ibx7 )Ϙ]R;m!J]Y%t_nq[ j|L&x5;3EuC0[l0@q!zL!<*%^O`0+)$ uIH|%'LL(J{9]EŦ£8" KPx#9OӀ|YV2XD{'|sRDi)X ŠSo\ºAX&zY4 Uh ]u1M؂ȸ;È0L,]}a*!yN"cY0o (.QBSq]̆F2pʦ1O*ڶ0]z`zJ5L[BA4lV\;Ia2 ˠlR K:UbWzgAK+Xim.Thڃ9ROu*ix"uHdڦ.o曁 "1=j58[p,3@dܜ9m@JPbP .t8dKC{Anb.JXx,g/+BN@Yw{!䤒آ%ĎNщ0ol6{~_-Kθ^pCFp-F\HF2@M@9#Q+]Vcec**Z[&93* bbQrէ%L  P k_N#^kQ\ -@ [%*$@&M,fDq[d2`ͱgFe81MXppsrN11J)^!>$l?D]f44eZf[ GG#%w~Q<_f쿠9^$ZZ;D/,/$̢W WvަBBg/",0 N;KFbNl5q?#> vz8t{@]5YIap_jwE w%VF& 1)tXc ~##IB˺DFLK^e˛:WvSd1 Hx[O䚮5n23;\]LX A,G[*l%MYW9چ%TuU{C9O;J@|QcBOX/p1fgpWBz5eodHӣ(+:C` B)ao !T/ãk,F$<"+0+Sb jѷbE\=E@C (*77JYDYB&Py񶅖^ 柅 )j l>A;9;LUvLZy; yjRq_$;}`RX)]> 6#;A&O3@$YtN8J ۶a>FqJν #x2TL5׀D1Ć .Wij$[4IBtV?Zn L LOA BBIh$>s}Xz,]}:L ʔ0+$s*ẕOKt8:Wd!=ͨȍzdYy0<9_/믉ͷ^ '[(I~:0`n S{9Vp"\&docg]ˋOp0fHsEaH@(}@':l+&?>6T+1QS1@.MD6Qez1#$F_ ӿۮYWf59:u H*lxkI;W鵦6]*|B i,- 戵L܀E~kD$5k"!4)9t\hCnB?s F X^!8hD'f54PK6&`J5tQ4xVy\ݒQV( ؚ|4^zqUoaʌ= 41aMA/⦛CaoÑ+ 1AWq\(,E- 9c5:O)ssvk;>bdf>E(g1ք}?B>[#97̝+׮`t!x+́gSI=rX+HWI☳BYme$ :xhub#iH1E9?+ [] vB%W>Um !&D &l9?H۩62-O Q # eΒ0:*Fru Tb\ [g"V#^h8猒.Wh,` Go$B`Q^eh iBʯIQ|Acم{7zjD!9PpOoԂP+@.|M3̢I,K#"q!#14ퟥH\H Upy?e6h?3} ;8?>G wJ]:QCGAkY1uYF_C"ʩthFF xL&^Q@[T ahl[_7} D ) ̘{Hr@v{D)MGĦF$VyցTDYԐ{Q߹]H۠Ą,}BÌ6x3> q6T`G.@ikKf'XCNX'Q[|7silaVuEW}>y|ƠIٲ.K$~/{=>gȖ 4nh v/j(HB Xnqۆ:c6u,e<8'EML!gFiG8ڦ WFSv=O}&#lyc4M7Sj%R/ë yXGW9Mg5{m#y`H(IK¥ϐ9+B r^m1Whܩ'CHLsQMBXρ(VT@ A*x:@8ozn5pU.$[ w5 8^ Cq%/RnpIvc>(4\uR?~lL%+Z~sNcm=(+#FkjCJݒN5%>DK}.~ \|g uNKjt]c>6+\eT 혇"_֨CEA]Մj=AׂY\f"7| ";I%C2ԢT"|fM:P@aQw?8:In{HaACv:Ri8 ;Mj>idߋKzgT"Ӗ%D uY|*.$Y(&[6{oEpY<[ň ]m=>d,s4{^H{FJw8C4}9*G$~V)j/R RW'c)B43w).ާpy`l?˦T&SyG%wE(_(bP𰠜x$cDjW^]`V\K+qR_;9FCLilu!*ٺ {Ѵ`? fY`<B"/$ժDxza%C# V1kVIGiC? 31}(Dpoy0-LQc{b;UYwNU6aP+H6N)f"ONx04CQM!$ E} .'K{]AOk `pe7o܄suKҶd$7UM/"9L!87SNr^H:#Sx+E(~+38eX#EZ0ej-ẹcڞN CZtZ4ܺ&e,4}>=٬FUN՗d񰿀FHCOxhbÜ,3&Md ֵÓ.XKjXyEQnYt 9m~K*}'8w§s8Z|Eٍ%1a%Oz7qX?=t9 :A9?ޮbltw-&Z:+# }y4gg pHKs .djdG%չ(f}:!W6t9ӓ3 (=㠉o}Lc:a/:T˟~SW1gVPTX+(Di6BhF7 ?Fվ⺥|)ܶ@uJ=_sfتliM Nw!"=ב׉k ,#;5W4TՠfD H4~+fimRS6p).,g*~ "zoeAl=uԷ|17g+q& EMHNb8h&̥ GųL3l!qh40`ap ?l`tW ߨ)D~p".^ƥOI0p{Bu?`2fqx_oL|ζaG0+Nj*FN]/߳F-wgz#ODセ^'vdwM>j-V An[gHZSo7Ll,?x. nU`S}A[xs8Z\xV8!jD3N{0! BKw]HИ|29hkĮvI(/~F{}_ёFk )Ip@K8$׃ݛ A ВZ~o`NJ>TP%*F~i 7 4"zH;$AXЊۈ7ө׳ϯYXm;&ml?,4к[ ڲe/3[aQ:,J;{k_&6,aɝ/xDżG~ƴF!xf]f׮;$RzLŗ~{,IT}ಉ.Sb٢ㅗ67[AVn`x#ur \Sd LUK۪KU)zD@LtfgE*]'t)V=``|)X] [<1j*;)vzђ5iyÕ` I9..ba !T4e h|S׸F-*KkJPTx8|Tڠ S;dۘ'Xv-Cs16?yx*[`!XiNGpc6ʍ8␶=+՛q־gVp1g‴'(9֭7vQ "&]b;g^@-~ [+hɻLî@]0~h h#: f2$z`Zy ,6ˎmW;'~+$ʎPU6=%/sm( ]x=H+~@Z҅Up5؞|,sIGPAWCI\UtHRknE!cX]`UiX^Za\ܨrb _:c g݄ی Ww:.L@Mx}] GcxZ?}&l2AۓS7mw>}+VZUڣQ𙅀H)l~P aK3T҇m~ϯb'L4=>rQI++#AMNa= q-%.9r<86^ZX;^#W bs+в :H[dKm;H;Mț0hٙ5wKj[JE"~{jfky*p,7>?(@7$ a9zו_"֯`@kf@đixccq'qӟ7Ƒzάxg; } LOFwMC4!X}\Z/[!Ⴋ)NrNz ͬR;Flf~T;>K$r-^r4lTԿ` Í ȍ>`[9c6ނ=0 LC"a7sn35/gRkH(1H\@?_=Y;ڥ_ ug]0"IC.}nY(ck8B؟AU4{ECWc`1\~^r>5cs$+,l3t\ <}B3BY8zbjr%nҳ? chkzJQts: k!&rWh Z7@R^.F?ׇz\ءP*1rcy&%R؉v`t3!SE'$/W3[Dt~튛6+I%rUƶaDwE8DAoU˖բN,%:oO0zݘGuj"f{0MgR-,t7fE{*d&~x@R_1;pѯ +c nb'RԅBS\嫭|PXq~n iTwךcFRv $[\3]++c޳URfa]hl揇Ós넵撒pL20^9/uLe9jOCYv(O8"(v5%4% jgX"d z xga)RT X, 6Uan8֚w̭ۋK-n "mx+$&'J(wFKFan` tL-{£bkCq=OqKY҈/Hk Z8C 1/=}‡p_Ckݶm-]Ym&T{`n֊~sz=vOWܟ = \=/AG{]lQlxcp+_DA]v{bhoUC\+oQ`T(:[ :8;NUD X3jkj$%sQ^qBv|16NU#fJ-EHtj)|ܨa ]V 0LF*h4utT^M#Y `wH UP,lB[~>vVnITQ!jxwmmJi9WEn\p#v-us`P TjBI V] vhz4;8^ӎQ\r#k;5s4FEkӽ=Z>74@RY첋YƕS>YDfL;Z!9.єh*R,5)P\|U; g.Aџz8zXEk:<܅ !ʾ㓫*( W.)E[v6lR>pHF\ "`;;P MhR\+Aj Dp_8YX_tV@ea K %30s{&}dQG(,V;(m^EȰYNkJ2ǻVi{4$\ d[̧ wϥiIxEv!0%x ]`H)ZMƷ+ ,&Y=D?g^$b,p<vx5}O@en~@3tU8ph}<Try ^- |E,[qA4oUr2,Uu2@y}Avl@\EaaӒ@8Є"իnqOr^mIVWXi i"dq3%ˣ$rsrVEN0;$ $}뷗U/+r4$b0ˆL֌ONI2jAuFoj#nLuihKKI*~\-dx(cp}h(kX-6Z`7Dyʣ1- 91 H ⾸6;1VYZƹf0j4WтY-p1K`BI0%#t puֳFם1 c-p"0SPE5 4p(BsP"_ӄd=pF-Rs}T_bw/O. /vQOݝnì:9pw7v5`w`eR3pQZPf xtD-;$Yt eLrI2-)̣K-@NA@-.FEn,!,d~4pɞP-I(ń GUK(@ٞWlЊO 𬂕(;;vlx^.MbXl(sfβb?!lQ&68]kS<8v͞p /Ok ;boBw= (p׿]G\հMs#Sj$qeן8"CRӇκw-wdG7\;e<%4@&lҒP)nGh 4d5Tb 0r[ލDm:69ozڐN VISwLsA9;]K faİo4痩9c"rlmd$̽|^h;H%4Z"재]#M괭e8Jef Vqeظqn̸`0V>+ah7ʠJ ~DFF9FÇ\thl~J(:=ܮHsa̽hA;Wt,Y=}GTefj>+ͼ_X&/ø>'c2 ' *'pZwp|/㯉|QjZ{ׯ^:k0k3=4w|77 MR8~d?Jz]x.&κz*"#U2 U5R>+ZuQLDc}!HwxLvՏBӴ,.eS:$Wߴ"JtYXNGm_j*7et]ˆJ$#Cw *~k;s\V+x"l"TBVNrVKSOlCc{upFg_fCS/]J]t {:CH"RH8lQ7̶r7P#^]xwu{]r}wIxwwo~}ߍ>]]Ewoo.G Qg8ھz,*C":bW]qvA%5:[i2Gl8|\\3QтU~V>&:DvE,Hjy#(Ej顯L?Jymu;bmF'.p<ӲVZ^}SVk 18Ʋ+"89_ \-<:G¾Í'z/)4ւ$r;<&(AdE_Ə鄘FqJR>!}RkhI"?<w`9U?Ql(U!ݐaok$G$T (* k-⴪dIA8RBPt4 U#3˶t|iR+yVG_dD 88Jъ'L:mqw -dzuKA2l5 *~j^u, ˃_㭧P®FO~?9y,Zk.QȋDKR2*3Г|ByVXyepg}ۊм渢J#8e^;Ts/u(w.MZj) oͳo \Xt$pؾ\I_F'υ/E )StPu1 6EP0E/펰`ț8%1czb(y66娮L!67 ౺q,~hO·DuŦڮLpVo|2 6|z4:Ydͩҷs W0w/,~+\4k%cp˸U0aK?^̻Ůι[̬{Z\[jv M(u050w,/ʫL2Z04 -ǾY-7/zT?4k0˹6O91Pym*-/:AvWh&- YtsUt:q\Dfp%RB.ǥIQ.E*ķڶ=?Rh!n%Qd=4$_E+jb 8fQ0+bx`{ wJN>BIVҨo/ZToh6hCZ'K&}fH=$kp '"]܊\3 q^ؗ!,yhߕ;E‰I'g&b-^RzgQuiym֏b ELVǺko`u2sP2,>p0l1R<׸6o>7ֶHHkYlGsWoY&X i$.V  [l,ȎZaI A6b: 1Ck1gH&=$ )f+,S͍ 1(+#195k'1bPM_&qXw\EL"ᵫ}(j aa5O}M!6 9Ϡ#Wz09vL"6.xy:Xq`akL6C+zzٍdIkS% K;T5R|,jZg(5N"m-FLB-O%iJ^n?f0socnetv-2.2/COPYING000644 000765 000024 00000105770 13040701535 015070 0ustar00dimitrisstaff000000 000000 You may use, distribute and copy SocNetV under the terms of GNU General Public License version 3, which is displayed below. GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . socnetv-2.2/INSTALL000644 000765 000024 00000005022 13040733126 015054 0ustar00dimitrisstaff000000 000000 Installation ============ Binaries SocNetV binary packages are available for Windows, Mac OS X and Linux distros. You can download a binary package for your Operating System from the project webpage at: http://socnetv.org/downloads If there is no package for your OS, please download and compile the source code. Windows To run SocNetV in Windows, download the latest SocNetV zip for Windows from the Downloads page, unzip it, and double-click on the socnetv executable. The program will run immediately. Mac OS X If you are a Mac user, you can download and run SocNetV from a disk image (dmg file). From the Downloads page, download the Mac OS .dmg file. Once downloaded, double click on it and a new window will appear. To run the application, double click on the SocNetV icon holding down the META key. To install SocNetV, drag the SocNetV icon to your Applications. Linux SocNetV is available in most Linux distributions, although not the latest version. To install the latest and greatest SocNetV version, users of openSUSE, Fedora and Ubuntu/Debian are advised to add our own repositories to their systems. In Debian and Ubuntu, install SocNetV from our repos with these commands: sudo add-apt-repository ppa:dimitris-kalamaras/ppa sudo apt-get update sudo apt-get install socnetv In Fedora and openSUSE, choose and add the correct repository from here: http://download.opensuse.org/repositories/home:/oxy86/ Once you add the repo, install SocNetV using the command (Fedora): sudo yum install socnetv or (openSUSE): sudo zypper in socnetv Compile from source code To compile and install SocNetV from source you need the Qt5 toolkit development libraries. Qt is an open source C++ toolkit published under the GPL. Qt5 is preinstalled in most Linux distributions and it is available for Windows and Mac OS X. If you do not have Qt5 installed, please download and install it from https://www.qt.io/developers Once you have Qt5 installed in your OS, you are ready to compile SocNetV from source. All you have to do is to type in the following commands in order to decompress the SocNetV tarball and build it. Replace 2.X with the version you downloaded. 1) untar zxfv SocNetV-2.X.tar.gz 2) cd socnetv-2.X 3) qmake 4) make 5) sudo make install Probably you have already done the first 2 steps, so just type in 'qmake' or 'qmake-qt5'. When you finish compiling and installing, run the application typing: socnetv or go to Start Menu > Mathematics > SocNetV. Questions ========= For any questions, email us at: info@socnetv.org socnetv-2.2/man/000755 000765 000024 00000000000 13040735147 014604 5ustar00dimitrisstaff000000 000000 socnetv-2.2/nets/000755 000765 000024 00000000000 13040701535 014774 5ustar00dimitrisstaff000000 000000 socnetv-2.2/NEWS000644 000765 000024 00000016477 13040701535 014541 0ustar00dimitrisstaff000000 000000 Social Networks Visualiser (SocNetV) SocNetV News -==========- Jan 2017 ======== - Version 2.2 released with major new features: Hierarchical Clustering Analysis (HCA) Pearson correlation coefficients Actor Similarities Tie profile dissimilarities Maximal clique census New network symmetrization methods: Strong Ties, Cocitation Multi-relational data read and write in GraphML GML format support Support for EdgeLists with labels Support for Pajek multirelational directed networks Adjacency matrix plotting Better reports (in HTML with JS) Improved performance and GUI Sep 2016 ======== - Version 2.1 released with a few new features but lots of bug fixes. This version brings a new algorithm for d-regular random network generation, and also a nice new dialog to control it. See ChangeLog for a complete log of new features and bugfixes. - Version 2.0 released with major code overhaul, new GUI layout and lots of bugfixes and improvements. The new version brings stability, great performance boost, and nice new features such as separate modes for graphs and digraphs, permanent settings/preferences functionality, edge labeling, recent files, keyboard shortcuts, etc. Also there are improvements in Force-Directed layouts, i.e. Fructherman-Reingold. See ChangeLog for a complete overview of the new features. - The SocNetV Manual is now build with Doxygen and it is available at http://socnetv.sf.net/documentation June 2015 ========= - Version 1.9 released with lots of bugfixes and a faster matrix inverse routine using LU decomposition. Also Information Centrality is greatly improved in terms of computation speed. PageRank Prestige algorithm corrected to compute PR using the correct formula. The initial PR score of each node is now 1/N. Bugs closed: #1463069 wrong average distance when there are isolates #1365037 certain sparse matrices crash socnetv on invertMatrix method #1365582 centralityInformation() is slow when network N>100 #1463095 edge filter works but the user cannot undo #1464422 wrong pagerank results #1464430 socnetv refuses to read pajek files not starting with *Network #1465774 edges do not always follow relations #1463082 edge color change is not taking place #1464418 socnetv crashes on pagerank computation on isolated nodes - Version 1.8 released with the following new features: New clique census routine to compute maximal cliques with up to 4 vertices. New Scale-free random generation methods. Improved Erdos-Renyi generation to include G(n,M) model. Fixed bug in Clustering Coefficient - SocNetV now computes CluCof correctly in all cases. New improved dialogs for easy random network generation (Scale-free, Erdos-Renyi, and Small-World) Fixed bug in Node Properties dialog. It is now populated with current node settings. May 2015 ======== - Version 1.7 released. New node group select/edit functionality and file previewer supporting different codecs - Version 1.6 released. New and improved web crawler functionality. See Changelog for more. Oct 2014 ======== - Version 1.5 released. First version with dijkstra algorithm for the SSSP in weighted nets. See Changelog for more. Sep 2014 ======== - Version 1.4 released. Brought new layout type (nodal size by prominence index), edgelist1 UCINET format import method and many bugfixes. Aug 2014 ======== - Version 1.3 released. - First time SocNetV works with multigraphs Aug 2014 ======= - Version 1.2 released. It features a major GUI overhaul and brings in a new "prominence indices" conceptualization based on Wasserman & Faust. In general, Centrality indices focus on outLinks (choices given) while Prestige indices consider inLinks (choices received). Added 3 Prestige indices (Degree, Proximity and PageRank), new reachability measures (Walks, Connectedness, and Reachability Matrix) and fixed a slew of bugs in indices calculation. All algorithms are now tested to report 100% correct results. - Version 1.1 released with major bug fixes. See ChangeLog. - First time distribution of a disk image for installation in Mac OS X Feb 2014 ======== - Version 1.0 released, starting a new 1.x series based on Qt5. The 0.x series is no longer maintained. Please upgrade :) - PageRank calculation and layout - SRS Documentation by Vagelis Motesnitsalis July 2013 ======== - Moved project code to git/BB - Started development for Qt5 Oct 2010 ======== - Version 0.90 released - New Power & Information Centralities Jan 2010 ======== - Version 0.80 - New List import feature - New Triad Census feature - Various Bug Fixes June 2009 ========= - Version 0.70 - First web crawler implementation May 2009 ======== - Version 0.6 (release) - GraphML becomes native SocNetV load format Feb 2009 ======= - Version 0.51 (bugfix release) - Version 0.50 (released) - Small world creation - Clustering coefficient - Exporting to PDF - Printing works OK. Jan 2009 ======== - Version 0.49 (released) - Ubuntu repository created. Sep 2008 ======== - New logo - New openSUSE package repo. - Version 0.48 released - Version 0.47 released - Version 0.46 released. Lots of bugfixes. New features: - Node sizes may reflect degree. Aug 2008 ======== - New Debian Package - Version 0.45 released. New features: - GraphML initial support. - New man page and updated online documentation. - HtmlViewer renders online help with the help of QtWebKit (openSUSE: libQtWebKit-devel) - New widget for network rotation. - New widget for zooming replaces the old one. - Nodes may have 4 different shapes: circles, diamonds, triangles, boxes and ellipses are supported. - There was a bug in Qt 4.3 QGraphicsView causing redraw delays. Is fixed in Qt 4.4 :) - Cosmetic changes, i.e. new icons, new layout for the left dock. - Code clean-up in MainWindows Class and Matrix. - Deleted obsolete members and functions such as nodeExists(), mousePosGW(), Dijkstra, etc. - Bug-fixes on loading Pajek networks and layout algorithm. May 2008 ======== - Version 0.44 released one year after v.0.43. New features: Ported to Qt4: Code rewritten almost from scratch. Splitted MainWindow/GUI from algorithms via a new Graph Class. Improved GUI with docks. Network zooming via mouse wheel. Spring Embedder: Dynamic network reallocation Thread support. Much faster calculation of distances and centralities (BFS/dijkstra). Betweenness centrality now is much more efficiently calculated. Changed license to GPL3 Layout in circles and levels by centrality. Better graphics and antialiasing (disabled - enable by pressing F8). New centrality index: Eccentricity. Sep 2006 ======== - version 0.43 released with new layout features. June 2006 ========= - version 0.42 released with updated help files. May 2006 ======== - Did some work on the webpages at http://socnetv.org. Hope it is better now. April 2006 ========== March 2006 ========== - version 0.41 released. February 2006 ------------- - version 0.40 released. Efforts to be a pretty trustworthy release. - sourceforge project downloads are more than enough daily, but there is no feedback yet for versions 0.38 and 0.39. - version 0.39 released. Somewhat rushed release. - constant changes in the homepage. - updated links in www.insna.org January 2006 ------------ - version 0.38 released after one year of silence. - The project moved to sourceforge.net - The homepage is http://socnetv.org socnetv-2.2/README.md000644 000765 000024 00000015250 13040733266 015313 0ustar00dimitrisstaff000000 000000 SocNetV -A Social Network Visualizer ==================================== \tableofcontents 1. Overview ------------ Social Network Visualizer (SocNetV) is a cross-platform, user-friendly and free software application for social network analysis and visualization. Using SocNetV you draw social networks (mathematical graphs) with a few clicks on a virtual canvas or load social network data of various formats (GraphML, GraphViz, Adjacency, Pajek, UCINET, etc). SocNetV enables you to modify the social networks, analyse their social and mathematical properties, produce reports for these properties and apply visualization layouts for relevant presentation of each network. The application supports multirelational loading and editing. You can load a network consisting of multiple relations or create a network on your own and add multiple relations to it. SocNetV computes graph-theoretic properties, such as density, diameter, geodesics and distances (geodesic lengths), connectedness, eccentricity, clustering coefficient, etc. It also supports advanced structural measures for social network analysis such as centrality and prestige indices (i.e. eigenvector and closeness centrality, betweenness centrality, information centrality, power centrality, proximity and pagerank prestige), community detection metrics (triad census, cliques, etc) and structural equivalence methods (hierarchical clustering analysis, similarities and dissimilarities, pearson coefficients, etc). Furthermore, random networks (Erdos-Renyi, Watts-Strogatz, ring lattice, etc) and well-known social network datasets (i.e. Padgett's Florentine families) can be easily recreated. SocNetV also offers a built-in web crawler, allowing you to automatically create networks from links found in a given initial URL. The application supports various layout algorithms based on either prominence indices (i.e. circular, level and nodal sizes by centrality score) or force-directed models (i.e. Eades, Fruchterman-Reingold, etc) for meaningful visualizations of your social network data. There is also comprehensive documentation, both online and while running the application, which explains each feature and algorithm of SocNetV in detail. SocNetV runs in Windows, Linux and Mac OS X. The program is Free Software, licensed under the GNU General Public License 3 (GPL3). You can copy it as many times as you wish, or modify it, provided you keep the same license. The documentation is also Free, licensed under the Free Documentation License (FDL). 2. Availability & License ------------------------- Official Website: http://socnetv.org Author: Dimitris V. Kalamaras Blog: http://dimitris.apeiro.gr SocNetV is a cross-platform application, developed in C++ language using the Qt5 cross-platform libraries and tools. This means you can compile and run SocNetV on Linux, Mac and Windows. SocNetV is Free Software, distributed under the General Public Licence Version 3 (see the COPYING file for details). The application is not a "finished" product. Therefore, there is no warranty of efficiency, correctness or usability. Nevertheless, we are looking forward to help you if you have any problem. See section 6 (bug reporting) below. 3. Installation --------------- You can install SocNetV : b) using binary packages. a) compiling it from source or ## a) Install a binary package or executable (Linux/Mac/Windows) SocNetV binary packages are available for Windows, Mac OS X and Linux distros. You can download a binary package for your Operating System from the project webpage at: http://socnetv.org/downloads If there is no package for your OS, please download and compile the source code. Windows To run SocNetV in Windows, download the latest SocNetV Windows installer from the project's Downloads page and double-click on the executable to start the installation. The program will be installed in the usual Windows Program Files directory and a new Start Menu shortcut will be created. Click on that shortcut to start SocNetV immediately. Mac OS X If you are a Mac user, you can download a SocNetV disk image (dmg file) to install it. From the Downloads page, download the Mac OS .dmg file. Once downloaded, double click on it and a new window will appear. Drag the SocNetV icon into your Applications folder to install it. To run the application, double click on the SocNetV icon holding down the META key. Linux SocNetV is available in most Linux distributions, although not the latest version. To install the latest and greatest SocNetV version, users of openSUSE, Fedora and Ubuntu/Debian are advised to add our own repositories to their systems. In Debian and Ubuntu, install SocNetV from our repos with these commands: sudo add-apt-repository ppa:dimitris-kalamaras/ppa sudo apt-get update sudo apt-get install socnetv In Fedora and openSUSE, choose and add the correct repository from here: http://download.opensuse.org/repositories/home:/oxy86/ Once you add the repo, install SocNetV using the command (Fedora): sudo yum install socnetv or (openSUSE): sudo zypper in socnetv ## b) Compile from Source Code To compile and install SocNetV from source you need the Qt5 toolkit development libraries. Qt is an open source C++ toolkit published under the GPL. Qt5 is preinstalled in most Linux distributions and it is available for Windows and Mac OS X. If you do not have Qt5 installed, please download and install it from https://www.qt.io/developers Once you have Qt5 installed in your OS, you are ready to compile SocNetV from source. Download the tarball archive with the source code of the latest SocNetV version (you probably already have this :P). All you have to do is to type in the following commands in order to decompress the SocNetV tarball and build it. Replace 2.X with the version you downloaded. 1) untar zxfv SocNetV-2.X.tar.gz 2) cd socnetv-2.X 3) qmake 4) make 5) sudo make install or su -c 'make install' Probably you have already done the first 2 steps, so just type in 'qmake' or 'qmake-qt5'. When you finish compiling and installing, run the application typing: socnetv or go to Start Menu > Mathematics > SocNetV. # 4. Command Line Options SocNetV is primarily a GUI program. Nevertheless, some command line options are available. Type: ./socnetv filename.net to start socnetv with network named filename.net loaded. # 5. Usage For usage documentation, see online help. Or, when running SocNetV, press F1 to display the SocNetV Manual. The manual is also available at the project's website. # 6. Bug reporting & contact Please, file any bug reports in our bug tracker: https://github.com/socnetv/app/issues To contact us, send an email to: info@socnetv.org socnetv-2.2/socnetv.desktop000755 000765 000024 00000000376 13040701535 017110 0ustar00dimitrisstaff000000 000000 [Desktop Entry] X-SuSE-translate=true Name=SocNetV GenericName=Social Networks Analysis and Visualisation Comment=Social Networks Visualisation and Analysis. Exec=socnetv Icon=socnetv Categories=Education;Science;Math;Qt; Terminal=false Type=Application socnetv-2.2/socnetv.pro000755 000765 000024 00000007235 13040701535 016240 0ustar00dimitrisstaff000000 000000 lessThan(QT_VERSION, 5.0) { error("SocNetV requires at least Qt 5.0!") } # START added for ArchLinux / openSUSE compatibility INSTALLPATH = / target.path = $$[INSTALLPATH]usr/bin TARGET = socnetv pixmap.path = $$[INSTALLPATH]usr/share/pixmaps pixmap.files = src/images/socnetv.png documentation.path = $$[INSTALLPATH]usr/share/doc/socnetv documentation.files = manual manpage.path = $$[INSTALLPATH]usr/share/man/man1 manpage.files = man/socnetv.1.gz translations.path = $$[INSTALLPATH]usr/share/socnetv translations.files = translations doc.path = $$[INSTALLPATH]usr/share/doc/socnetv doc.files = license changelog.gz NEWS README.md TODO COPYING AUTHORS INSTALL INSTALLS += target pixmap documentation manpage translations doc # END TEMPLATE = app CONFIG += qt thread warn_on release #CONFIG += qt thread warn_on release debug LANGUAGE = C++ # support QT += xml QT += network QT += widgets QT += printsupport INCLUDEPATH += ./src FORMS += src/forms/dialogfilteredgesbyweight.ui \ src/forms/webcrawlerdialog.ui \ src/forms/dialognodeedit.ui \ src/forms/dialogdatasetselect.ui \ src/forms/dialograndsmallworld.ui \ src/forms/dialograndscalefree.ui \ src/forms/dialogranderdosrenyi.ui \ src/forms/dialograndregular.ui \ src/forms/dialogsettings.ui \ src/forms/dialogsimilaritypearson.ui \ src/forms/dialogsimilaritymatches.ui \ src/forms/dialogdissimilarities.ui \ src/forms/dialogclusteringhierarchical.ui HEADERS += src/guide.h \ src/graphicswidget.h \ src/edge.h \ src/edgeweight.h \ src/edgelabel.h \ src/graph.h \ src/mainwindow.h \ src/matrix.h \ src/node.h \ src/nodelabel.h \ src/nodenumber.h \ src/texteditor.h \ src/vertex.h \ src/parser.h \ src/dialogfilteredgesbyweight.h \ src/webcrawlerdialog.h \ src/webcrawler.h \ src/dialogdatasetselect.h \ src/dialogpreviewfile.h \ src/dialognodeedit.h \ src/dialogranderdosrenyi.h \ src/dialograndsmallworld.h \ src/dialograndscalefree.h \ src/dialograndregular.h \ src/dialogsettings.h \ src/dialogsimilaritypearson.h \ src/dialogsimilaritymatches.h \ src/dialogdissimilarities.h \ src/dialogclusteringhierarchical.h SOURCES += src/guide.cpp \ src/graphicswidget.cpp \ src/edge.cpp \ src/edgeweight.cpp \ src/edgelabel.cpp \ src/graph.cpp \ src/main.cpp \ src/mainwindow.cpp \ src/matrix.cpp \ src/node.cpp \ src/nodelabel.cpp \ src/nodenumber.cpp \ src/texteditor.cpp \ src/vertex.cpp \ src/parser.cpp \ src/dialogfilteredgesbyweight.cpp \ src/webcrawlerdialog.cpp \ src/webcrawler.cpp \ src/dialogdatasetselect.cpp \ src/dialogpreviewfile.cpp \ src/dialognodeedit.cpp \ src/dialogranderdosrenyi.cpp \ src/dialograndsmallworld.cpp \ src/dialograndregular.cpp \ src/dialograndscalefree.cpp \ src/dialogsettings.cpp \ src/dialogsimilaritypearson.cpp \ src/dialogsimilaritymatches.cpp \ src/dialogdissimilarities.cpp \ src/dialogclusteringhierarchical.cpp # Extra optimization flags #win32 { # QMAKE_CXXFLAGS += -msse -mfpmath=sse -ffast-math #} #unix:!macx{ # QMAKE_CXXFLAGS += -ffast-math #} #macx { # QMAKE_CXXFLAGS += -msse -ffast-math #} RESOURCES = src/src.qrc win32 { RC_FILE = src/icon.rc } macx:ICON = src/images/socnetv.icns TRANSLATIONS = translations/socnetv_es.ts \ translations/socnetv_el.ts socnetv-2.2/socnetv.spec000644 000765 000024 00000020352 13040735130 016360 0ustar00dimitrisstaff000000 000000 # spec file for package socnetv # # Copyright (c) 2016 Dimitris Kalamaras dimitris.kalamaras@gmail.com # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed # upon. The license for this file, and modifications and additions to the # file, is the same license as for the pristine package itself (unless the # license for the pristine package is not an Open Source License, in which # case the license is the MIT License). An "Open Source License" is a # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. # Please submit bugfixes or comments via http://bugs.opensuse.org/ %define name socnetv %define version 2.2 %define release 1 %define prefix /usr/local %define lastrev %(LANG=en_US.UTF-8 && date +"%a %b %e %Y") %define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) %define is_fedora %(test -e /etc/fedora-release && echo 1 || echo 0) %define qmake qmake %define lrelease lrelease #BEGIN BUILDSERVICE COMMANDS %if 0%{?fedora_version} %define is_suse 0 %define is_fedora 1 %endif %if 0%{?suse_version} %define is_suse 1 %define is_fedora 0 %endif #END BUILDSERVICE COMMANDS %if %{is_fedora} %define distr Fedora %define breqr qt5-qtbase,qt5-qtbase-devel, qt5-qttools, fedora-release, desktop-file-utils %define qmake /usr/bin/qmake-qt5 %define lrelease /usr/bin/lrelease %endif %if %{is_suse} %define distr SUSE # %(head -1 /etc/SuSE-release) %define breqr libqt5-qtbase, libqt5-qtbase-devel, libqt5-qttools, update-desktop-files %define qmake /usr/bin/qmake-qt5 %define lrelease /usr/bin/lrelease %endif Name: %{name} Version: %{version} Release: %{release} Summary: A Social Networks Analyser and Visualiser License: GPL-3.0 Group: Productivity/Scientific/Math URL: http://socnetv.org/ Vendor: Dimitris V. Kalamaras Source0: SocNetV-%{version}.tar.bz2 Distribution: %{distr} Prefix: %{prefix} BuildRequires: gcc-c++, %{breqr} BuildRequires: pkgconfig(Qt5Core) BuildRequires: pkgconfig(Qt5Gui) BuildRequires: pkgconfig(Qt5PrintSupport) BuildRequires: pkgconfig(Qt5Widgets) BuildRequires: pkgconfig(Qt5Network) BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot # #DESCRIPTION SECTION # %description SocNetV (Social Network Visualiser) is a flexible, user-friendly free software application for social network analysis and visualisation. It lets you create new networks (graphs) with a few clicks on a virtual canvas or load networks of various formats (GraphViz, GraphML, Adjacency, Pajek, etc) and modify them to suit your needs. The application computes graph theory metrics, such as density, diameter and distances (shortest paths) in directed and undirected, weighted or non weighted graphs. It also computes node and network centrality and prestige indices, such as closeness, betweeness, eigenvector, information, power centralities and pagerank prestige. Community detection and structural equivalence algorithms are included, such as triad census, clique census, hierarchical cluster analysis, actor similarity and tie profile dissimilarities. Various layout algorithms (i.e. Spring-embedder, circular and in levels according to centrality or prestige) are supported for meaningful visualisations of your networks. Furthermore, SocNetV generates random networks using various models such as Erdos-Renyi, Scale-Free, Small-World, d-regular etc. The application also includes a simple web crawler to create a social network of web pages, where edges are the links between them. Author: Dimitris V. Kalamaras # #PREPARATION SECTION # %prep %setup chmod -R a-x+X COPYING changelog.gz INSTALL NEWS README.md TODO man nets src chmod 644 nets/* find . -type f -name '*~' -delete find . -type f -name '*.bak' -delete rm -f config.log config.status Makefile socnetv.spec socnetv.mak sed -i -e 's/INSTALLPATH = \//INSTALLPATH = ./g' socnetv.pro # #MAKE SECTION # %build %{qmake} %__make # #INSTALL SECTION # %install %if %{is_fedora} desktop-file-validate %{name}.desktop #desktop-file-install --add-category="Math" --delete-original --dir=%{buildroot}%{_datadir}/applications %{buildroot}/%{_datadir}/applnk/Edutainment/%{name}.desktop %endif %makeinstall mkdir -p %{buildroot}%{_bindir} mkdir -p %{buildroot}%{_datadir}/pixmaps/ mkdir -p %{buildroot}%{_datadir}/applications/ mkdir -p %{buildroot}%{_mandir}/man1/ cp -r socnetv %{buildroot}%{_bindir}/%{name} cp -r src/images/socnetv.png %{buildroot}%{_datadir}/pixmaps/%{name}.png cp -r socnetv.desktop %{buildroot}%{_datadir}/applications/ cp -r man/socnetv.1.gz %{buildroot}%{_mandir}/man1 rm -rf %{buildroot}/%{_datadir}/doc/%{name} %clean [ -d %{buildroot} -a "%{buildroot}" != "" ] && %__rm -rf %{buildroot} # #FILES SECTION # %files %defattr(-,root,root) %{_bindir}/%{name} %{_datadir}/applications/%{name}.desktop %{_datadir}/pixmaps/%{name}.png %{_mandir}/man1/* %doc changelog.gz NEWS README.md TODO COPYING AUTHORS INSTALL # #CHANGELOG SECTION # %changelog * Sat Jan 21 2017 Dimitris Kalamaras - 2.2-1 - Synced with new stable version 2.2 from upstream * Wed Sep 28 2016 Dimitris Kalamaras - 2.1-1 - Synced with new stable version from upstream. * Tue Sep 13 2016 Dimitris Kalamaras - 2.0-2 - Spec patch for Buildservice * Mon Sep 12 2016 Dimitris Kalamaras - 2.0-1 - Synced with new stable version from upstream. * Tue Jun 23 2015 Dimitris Kalamaras - 1.9-1 - Synced with DEV version from upstream. * Fri Jun 05 2015 Dimitris Kalamaras - 1.8-1 - Synced with new stable version from upstream. * Wed May 20 2015 Dimitris Kalamaras - 1.7-1 - Synced with new stable version from upstream. * Mon May 11 2015 Dimitris Kalamaras - 1.6-1 - Synced with new stable version from upstream. * Fri Oct 10 2014 Dimitris Kalamaras - 1.5-1 - Synced with new stable version from upstream. * Mon Sep 01 2014 Dimitris Kalamaras - 1.4-1 - Synced with new stable version from upstream. * Wed Aug 27 2014 Dimitris Kalamaras - 1.3-1 - Synced with new stable version from upstream. * Mon Aug 18 2014 Dimitris Kalamaras - 1.2-1 - Synced with new stable version 1.2 from upstream. * Fri Aug 01 2014 Dimitris Kalamaras - 1.1-1 - Synced with new version from upstream. * Thu Feb 27 2014 Dimitris Kalamaras - 1.0-2 - Fixed spec for openSUSE * Thu Feb 27 2014 Dimitris Kalamaras - 1.0-1 - Synced with new version from upstream. * Thu Oct 14 2010 Dimitris Kalamaras - 0.90-1 - Synced with upstream. * Thu Jan 28 2010 Dimitris Kalamaras - 0.81-1 - Synced with upstream. - Bugfixes for Windows version * Sat Jan 09 2010 Dimitris Kalamaras - 0.80-1 - Synced with upstream, * Mon Jun 29 2009 Dimitris Kalamaras - 0.70-1 - Synced with upstream * Wed May 27 2009 Dimitris Kalamaras - 0.6.0-1 - Synced with upstream * Thu Feb 26 2009 Dimitris Kalamaras - 0.52-1 - Synced with upstream. - Bugfixes into .spec.in for RPMs (Fedora, openSUSE and Mandriva). * Tue Feb 17 2009 Dimitris Kalamaras - 0.51-3 - Bugfixes into .spec.in for Fedora and Mandriva. - RPM for Fedora * Mon Feb 16 2009 Dimitris Kalamaras - 0.51-2 - Minor changes to RPM * Mon Feb 16 2009 Dimitris Kalamaras - 0.51-1 - Updated to upstream version 0.51 * Fri Feb 13 2009 Dimitris Kalamaras - 0.50-1 - Updated to upstream version 0.50 * Wed Jan 14 2009 Dimitris Kalamaras - 0.49-2 - Package .spec fixes * Tue Jan 13 2009 Dimitris Kalamaras - 0.49-1 - Updated to 0.49 * Wed Sep 17 2008 Dimitris Kalamaras - 0.48-1 - First RPM release socnetv-2.2/src/000755 000765 000024 00000000000 13040735147 014620 5ustar00dimitrisstaff000000 000000 socnetv-2.2/TODO000644 000765 000024 00000000540 13040701535 014512 0ustar00dimitrisstaff000000 000000 KNOWN BUGS ========== 1: Negative weights break centralities 2: UCINET (DL) files in nodelist1 format (one-mode) are not supported. 3. GraphML files containing special HTML chars (i.e. & ) in labels are not readable. TODO ==== To request a feature and/or see the TODO list of SocNetV, visit : https://blueprints.launchpad.net/socnetv socnetv-2.2/translations/000755 000765 000024 00000000000 13040701535 016544 5ustar00dimitrisstaff000000 000000 socnetv-2.2/translations/socnetv_de.ts000644 000765 000024 00000623230 13040701535 021253 0ustar00dimitrisstaff000000 000000 HTMLViewer &File Datei &Open Öffnen Ctrl+O Strg+O Opens another helpfile Öffnet andere Hilfequelle &Print Drucken Ctrl+P Strg+P Prints out the actual network Druckt das aktuelle Netzwerk E&xit Beenden Ctrl+X Strg+X Close Manual Schließe Handbuch &Back Zurück Ctrl+B Strg+B &Backward Rückwärts &Forward Vorwärts Ctrl+F Strg+F &Home Start Ctrl+H Strg+H &Go Los MainWindow Welcome to Social Networks Visualiser, Version Willkommen zu Social Networks Visualiser, Version &New Neu Ctrl+N Strg+N Creates a new network Erstellt neues Netzwerk New network (Ctrl+N) Neues Netzwerk (Strg+N) New Creates a new network Neu Erstellt neues Netzwerk &Open Öffnen Ctrl+O Strg+O Open network (Ctrl+O) Öffne Netzwerk (Strg+O) Opens a a file of an existing network Öffnet Datei mit existierendem Netzwerk Open Opens a file of an existing network Öffnen Öffnet Datei mit existierendem Netzwerk &Save Speichern Ctrl+S Strg+S Save network (Ctrl+S) Speichere Netzwerk (Srtg+S) Saves the actual network to the current file Speichert aktuelles Netzwerk aktueller Datei Save. Saves the actual network Speichern Speichert aktuelles Netzwerk Save &As... Speichern unter Ctrl+Shift+S Strg+Shift+S Saves the actual network under a new filename Speichere aktuelles Netzwerk unter neuer Datei Save As Saves the actual network under a new filename Speichern unter Speichert aktuelles Netzwerk unter neuer Datei &BMP... BMP Export network to a BMP image Exportiere Netzwerk als BMP Bild Export BMP Export network to a BMP image Export BMP Exportiere Netzwerk als BMP Bild &PNG... PNG Export network to a PNG image Exportiere Netzwerk als PNG Bild Export PNG Export network to a PNG image Export PNG Exportiert Netzwerk als PNG Bild &PDF... PDF Export network to a PDF file Exportiere Netzwerk als PDF Datei Export PDF Export network to a PDF document Export PDF Exportiere Netzwerk als PDF Dokument &Adjacency Matrix Adjacency Matrix Export network to an adjacency matrix file Exportiere Netzwerk als Adjacency Matrix Datei Export Sociomatrix Export network to a adjacency matrix-formatted file Export Sociomatrix Exportiere Netzwerk als Adjacency Matrix-formatierte Datei &Pajek Pajek Export network to a Pajek-formatted file Exportiere Netzwerk als Pajek-formatierte Datei Export Pajek Export network to a Pajek-formatted file Export Pajek Exportiere Netzwerk als Pajek-formatierte Datei &List List Export network to a List-formatted file. Exportiere Netzwerk als List-formatierte Datei. Export List Export network to a List-formatted file Export List Exportiere Netzwerk als List-formatierte Datei &DL... DL... Export network to a DL-formatted file Exportiere Netzwerk als DL-formatierte Datei Export DL Export network to a DL-formatted Export DL Exportiere Netzwerk als DL-formatierte Datei &GW... GW... Export network to a GW-formatted file Exportiere Netzwerk als GW-formatierte Datei Export Export network to a GW formatted file Exportiere Exportiere Netzwerk als GW-formatierte Datei &Close Schließen Closes the actual network Schließt das aktuelle Netzwerk Close Closes the actual network Schließen Schließt das aktuelle Netzwerk &Print Drucken Ctrl+P Strg+P Prints whatever is viewable on the canvas. Druckt sichtbaren Bereich Printing This function prints whatever is viewable on the canvas. To print the whole network, you might want to zoom-out. Drucken Diese Funktion druckt den sichtbaren Bereich. Um das gesamte Netzwerk zu drucken ggf. herauszoomen E&xit Beenden Ctrl+Q Strg+Q Quits the application Beendet die Anwendung Exit Quits the application Beenden Beendet die Anwendung View Loaded File Betrachte geladene Datei F5 F5 Displays the loaded network file Zeigt geladene Netzwerk-Datei an View Loaded File Displays the file of the loaded network Betrachte geladene Datei Zeigt die geladene Netzwerk-Datei an View Adjacency Matrix Zeige Adjacency Matrix F6 F6 Displays the adjacency matrix of the active network Zeigt Adjacency Matrix des aktiven Netzwerkes View Network file Displays the adjacency matrix of the active network Zeige Netzwerk-Datei an Zeigt die Adjacency Matrix des aktiven Netzwerkes Erdos-Renyi G(n,p) Erdos-Renyi G(n,p) Shift+U Shift+U Creates a random network where each edge is included with a given probability Erschafft ein zufälliges Netzwerk in dem jede Kante mit gegebener Wahrscheinlichkeit eingeschlossen ist Uniform Creates a random network of G(n, p) model by connecting nodes randomly. Each edge is included in the graph with equal probability p, independently of the other edges Uniform Erschafft ein zufälliges Netzwerk im G(n, p) Model durch zufälliges Verbinden der Knoten. Jede Kante ist mit gleicher Wahrscheinlichkeit p im Graphen eingschlossen, unabhängig von den anderen Kanten Connected Verbunden Creates a connected random network Erschafft ein verbundenes zufälliges Netzwerk Uniform Connected Creates a connected random network Uniform Connected Erschafft ein verbundenes zufälliges Netzwerk Ring Lattice Ring Lattice Shift+L Shift+L Creates a ring lattice random network Erschafft ein zufälliges Ring Lattice Netzwerk Ring Lattice A ring lattice or a physicist's lattice is a graph with N nodes each connected to K neighbors, K / 2 on each side. Ring Lattice Ein Ring Lattice oder Physicist' Lattice ist ein Graph mit N Knoten, jeder verbunden mit K Nachbarn, K / 2 auf jeder Seite. Same Degree Same Degree Creates a random network where all nodes have the same degree. Erschafft ein Netzwerk in dem alle Knoten den gleichen Grad besitzen. Same Degree Creates a random network where all nodes have the same degree Same Degree Erschafft ein zufälliges Netzwerk in dem alle Knoten den selben Grad besitzen Gaussian Gaussian Creates a Gaussian distributed random network Erschafft ein zufälliges normalverteiltes Netzwerk Gaussian Creates a random network of Gaussian distribution Gaussian Erschafft ein zufälliges Netzwerk mit Gauß'scher Verteilung Small World Small World Shift+W Shift+W Creates a random network with small world properties Erschafft ein zufälliges Netzwerk mit Small World Eigenschaften Small World A Small World, according to the Watts and Strogatz model, is a random network with short average path lengths and high clustering coefficient. Small World Eine Small World, nach Watts' und Strogatz' Model, ist ein zufälliges Netzwerk mit kurzen mittleren Pfadlängen und hohem Clustering Koeffizienten Find Node Finde Knoten Ctrl+F Strg+F Finds and highlights a node by number or label. Press Ctrl+F again to undo. Findet einen Knoten nach Nummer oder Label und hebt ihn hervor. Umschalten mit Strg+F Find Node Finds a node with a given number or label and doubles its size. Ctrl+F again resizes back the node Finde Knoten Findet einen Knoten mit gegebener Nummer oder Label und verdoppelt dessen Größe. Strg+F setzt die Skalierung wieder zurück Add Node Füge Knoten hinzu Ctrl+A Strg+A Adds a node Fügt einen Knoten hinzu Add Node Adds a node to the network Füge Knoten hinzu Fügt dem Netzwerk einen Knoten hinzu Remove Node Entferne Knoten Ctrl+Shift+A Strg+Shift+A Removes a node Entfernt einen Knoten Remove Node Removes a node from the network Entferne Knoten Entfernt einen Knoten des Netzwerkes Change Label Ändere Label Changes the Label of a node Ändert das Label eines Knotens Change Label Changes the label of a node Ändere Label Ändert das Label eines Knotens Change Color Ändere Farbe Changes the color of a node Ändert die Farbe eines Knotens Change Color Changes the Color of a node Ändere Farbe Ändert die Farbe eines Knotens Change Size Ändere Größe Changes the actual size of a node Ändert die Größe eines Knotens Change Size Changes the actual size of a node Ändere Größe Ändert die Größe eines Knotens Change Value Ändere Wert Changes the value of a node Ändert den Wert eines Knotens Change Value Changes the value of a node Ändere Wert Ändert den Wert eines Knotens Change all Nodes Size Ändere Größe aller Knoten This option lets you change the size of all nodes Diese Option erlaubt die Größe aller Knoten zu ändern Nodes Size This option lets you change the size of all nodes Knoten Größe Dies Option erlaubt die Größe aller Knoten zu ändern Change all Nodes Shape Ändere Form aller Knoten This option lets you change the shape of all nodes Diese Option erlaubt die Form aller Knoten zu ändern Nodes Shape This option lets you change the shape of all nodes Knoten Form Diese Option erlaubt die Form aller Knoten zu ändern Change Node Shape to Box Ändere Knotenform zu Box This option lets you change the shape of a node to a box Diese Option erlaubt die Form eines Knotens zu Box zu ändern Node as a box This option lets you change the shape of a node to a box Knoten als Box Diese Option erlaubt die Form eines Knotens zu Box zu ändern Change Node Shape to Triangle Ändere Knotenform zu Dreieck Change Node Shape to Circle Ändere Knotenform zu Kreis Change Node Shape to Diamond Ändere Knotenform zu Diamant Change Node Shape to Ellipse Ändere Knotenform zu Ellipse Change all Numbers Size Ändere Größe aller Nummern It lets you change the font size of the numbers of all nodes Erlaubt die Schrifftgröße der Nummern aller Knoten zu ändern Numbers Size Changes the size of the numbers of all nodes Nummern Größe Ändert die Größe der Nummern aller Knoten Change all Labels Size Ändere Größe aller Labels You can change the font size of the labels of all nodes Erlaubt die Schrifftgröße der Labels aller Knoten zu ändern Labels Size Change the fontsize of the labels of all nodes Label Größe Ändere die Schrifftgröße der Label aller Knoten Add Link Füge Link hinzu Ctrl+L Strg+L Adds a Link to a Node Fügt einem Knoten einen Link hinzu Add Link Adds a Link to the network Fügt dem Netzwerk einen Link hinzu Remove Entfernen Ctrl+Shift+L Strg+Shift+L Removes a Link Entfernt einen Link Remove Link Removes a Link from the network Entferne Link Entfernt einen Link vom Netzwerk Changes the Label of a Link Ändere Label eines Links Change Label Changes the label of a Link Ändere Label Ändert das Label eines Links Changes the Color of a Link Ändert die Farbe eines Links Change Color Changes the Color of a Link Ändere Farbe Ändert die Farbe eines Links Change Weight Ändere Gewichtung Changes the Weight of a Link Ändert die Gewichtung eines Links Change Value Changes the Weight of a Link Ändere Wert Ändert die Gewichtung eines Links Filter Nodes Filtere Knoten Filters Nodes of some value out of the network Filtert Knoten eines bestimmten Wertes aus dem Netzwerk Filter Nodes Filters Nodes of some value out of the network. Filter Knoten Filtert Knoten eines bestimmten Wertes aus dem Netzwerk Filter Links Filtere Links Filters Links of some weight out of the network Filtert Links einer bestimmten Gewichtung aus dem Netzwerk Filter Links Filters Link of some specific weight out of the network. Filter Links Filtert Links einer bestimmten Gewichtung aus dem Netzwerk Change Background Color Ändere Hintergrundfarbe Click to change the background color Klicken um Hintergrundfarbe zu ändern Background Changes background color Hintergrund Ändert Hintergrundfarbe Change all Nodes Colors Ändere Farbe aller Knoten Click to choose a new color for all nodes. Klicken um neue Farbe für alle Knoten zu wählen All Nodes Changes all nodes color at once. Alle Knoten Ändert die Farbe aller Knoten. Change all Numbers Colors Ändere Farbe aller Nummern Click to change the color of all numbers. Klicken um Farbe aller Nummern zu ändern Numbers Changes the color of all numbers. Nummern Ändert die Farbe aller Nummern Change all Labels Colors Ändere Farbe aller Label Click to change the color of all node labels. Klicken um Farbe aller Labels zu ändern Numbers Changes the color of all node labels. Nummern Ändert die Farbe aller Label Change all Links Colors Ändere Farbe aller Links Click to change the color of all links. Klicken um Farbe aller Links zu ändern Background Changes all links color Hintergrund Ändert die Farbe aller Links Transform Nodes to Links Transformiere Knoten zu Link Transforms the network so that nodes become links and vice versa Transformiert das Netzwerk so dass Knoten zu Links werden und umgekehrt Transform Nodes LinksAct Transforms network so that nodes become links and vice versa Transformiere Knoten LinksAct Transformiert das Netzwerk so dass Knoten zu Links werden und umgekehrt Symmetrize Links Mache Links symmetrisch Shift+R Shift+R Makes all edges reciprocal (thus, a symmetric graph). Macht alle Kanten reziprok (d.h. einen symmetrischen Graphen) Symmetrize Edges Transforms all arcs to double links (edges). The result is a symmetric network Mache Kanten symmetrisch Transformiert alle Bögen zu doppelten Links (Kanten). Das Ergebnis ist ein symmetrisches Netzwerk Strong Structural Strong Structural Nodes are assigned the same color if they have identical in and out neighborhoods Knoten erhalten die selbe Farbe wenn sie identische In- und Out-Nachbarn besitzen Click this to colorize nodes; Nodes are assigned the same color if they have identical in and out neighborhoods Klicken um Knoten zu färben; Knoten erhalten die selbe Farbe wenn sie identische In- und Out-Nachbarn besitzen Regular Regular Nodes are assigned the same color if they have neighborhoods of the same set of colors Knoten erhalten die selbe Farbe wenn sie Nachbarschaften gleicher Farbsets besitzen Click this to colorize nodes; Nodes are assigned the same color if they have neighborhoods of the same set of colors Klicken um Knoten zu färben; Knoten erhalten die selbe Farbe wenn sie Nachbarschaften gleicher Farbsets besitzen Random Random Repositions the nodes in random places Setzt die Knoten in zufälliger Verteilung zurück Random Layout Repositions the nodes in random places Zufälliges Layout Setzt die Knoten in zufälliger Verteilung zurück Random Circle Zufälliger Kreis Repositions the nodes randomly on a circle Verteilt die Knoten zufällig auf einen Kreis Random Circle Layout Repositions the nodes randomly on a circle Kreis Layout Verteilt die Knoten zufällig auf einen Kreis In-Degree In-Degree Ctrl+1 Strg+1 Repositions the nodes on circles of different radius. More In-Degree Central Nodes are positioned towards the centre. Circle In-Degree Centrality Layout Repositions the nodes on circles of different radius. More In-Degree Central Nodes are positioned towards the centre. Out-Degree Ctrl+2 Repositions the nodes on circles of different radius. More Out-Degree Central Nodes are positioned towards the centre. Circle Out-Degree Centrality Layout Repositions the nodes on circles of different radius. More Out-Degree Central Nodes are positioned towards the centre. Closeness Ctrl+3 Repositions the nodes on circles of different radius. More Closeness Central Nodes are positioned towards the centre. Circle Closeness Centrality Layout Repositions the nodes on circles of different radius. More Closeness Central Nodes are positioned towards the centre. Betweenness Ctrl+4 Repositions the nodes on circles of different radius. More Betweenness Central Nodes are positioned towards the centre. Circle Betweenness Centrality Layout Repositions the nodes on circles of different radius. More Betweenness Central Nodes are positioned towards the centre. Informational Ctrl+5 Repositions the nodes on circles of different radius. More Informational Central Nodes are situated towards the centre. Circle Informational Centrality Layout Repositions the nodes on circles of different radius. More Informational Central Nodes are positioned towards the centre. Stress Ctrl+6 Repositions the nodes on circles of different radius. More Stressed Central Nodes are positioned towards the centre. Circle Stress Centrality Layout Repositions the nodes on circles of different radius. Nodes having greater Stress Centrality are situated towards the centre. Graph Ctrl+7 Repositions the nodes on circles of different radius. More Graphed Central Nodes are positioned towards the centre. Circle Graph Centrality Layout Repositions the nodes on circles of different radius. Nodes having greater Graph Centrality are situated towards the centre. Eccentricity Ctrl+8 Repositions the nodes on circles of different radius. Nodes of large eccentricity are positioned towards the centre. Circle Eccentricity Centrality Layout Repositions the nodes on circles of different radius. Nodes having greater Eccentricity Centrality are situated towards the centre. Remove Layout GuideLines Removes Red GuideLines from the canvas. Remove GuideLines Removes any guidelines (circles or horizontal lines) created for the network layout. Ctrl+Shift+1 Repositions the nodes on levels of different height. More In-Degree Central Nodes are situated on higher levels. Level In-Degree Centrality Layout Repositions the nodes on levels of different height. More In-Degree Central Nodes are situated on higher levels. Ctrl+Shift+2 Repositions the nodes on levels of different height. More Out-Degree Central Nodes are situated on higher levels. Level Out-Degree Centrality Layout Repositions the nodes on levels of different height. More Out-Degree Central Nodes are situated on higher levels. Ctrl+Shift+3 Repositions the nodes on levels of different height. More Closeness Central Nodes are situated on higher levels. level Closeness Centrality Layout Repositions the nodes on levels of different height. More Closeness Central Nodes are situated on higher levels. Ctrl+Shift+4 Repositions the nodes on levels of different height. More Betweenness Central Nodes are situated on higher levels. level Betweenness Centrality Layout Repositions the nodes on levels of different height. More Betweenness Central Nodes are situated on higher levels. Ctrl+Shift+5 Repositions the nodes on levels of different height. More Informational Central Nodes are situated on higher levels. Level Informational Centrality Layout Repositions the nodes on levels of different height. More Informational Central Nodes are situated on higher levels. Spring Embedder Alt+1 All nodes repel each other while the connected ones are attracted as if connected by springs. Spring Embedder Layout In this model, nodes are regarded as physical bodies (i.e. electrons) which exert repelling forces to each other, while edges are springs connecting adjacents nodes. Non-adjacent nodes repel each other while connected nodes are The algorithm continues until the system retains an equilibrium state in which all forces cancel each other. Fruchterman-Reingold Alt+2 Repelling forces between all nodes, and attracting forces between adjacent nodes. Fruchterman-Reingold Layout Embeds a layout all nodes according to a model in which repelling forces are used between every pair of nodes, while attracting forces are used only between adjacent nodes. The algorithm continues until the system retains its equilibrium state where all forces cancel each other. Zoom &in Ctrl++ Zoom in (Ctrl++) Zooms inside the actual network. Zoom In. Zooms in. What else did you expect? Zoom &out Ctrl+- Zoom out (Ctrl+-) Zooms out of the actual network. Zoom out. Zooms out. What else did you expect? NodeSize = F (OutDegree) Alt+3 Resizes all nodes according to their out edges. NodeSize = F (OutDegree) Adjusts the size of each node according to their out-edges (OutDegree). The more out-likned a node is, the bigger will appear... NodeSize = F (InDegree) Alt+4 Resizes all nodes according to their in edges. NodeSize = F (InDegree) This method adjusts the size of each node according to their in-edges (InDegree). The more in-linked a node is, the bigger will appear... Network Symmetry Shift+S Tests if the network is symmetric or not Network Symmetry A network is symmetric when all edges are reciprocal, or, in mathematical language, when the adjacency matrix is symmetric. Graph Distance Ctrl+G Calculates the length of the shortest path between two nodes... Graph Distance The graph distance (or geodesic distance) of two nodes is the length (number of edges) of the shortest path between them. Distance &Matrix Ctrl+M Displays the matrix of graph distances between all nodes Distance Matrix A distance matrix is a NxN matrix, where the (i,j) element is the graph distance from node i to node j. Diameter Ctrl+D Calculates and displays the diameter of the network. Diameter The Diameter of a network is the maximum graph distance (maximum shortest path length) between any two nodes of the network. Average Graph Distance Ctrl+B Calculates and displays the average shortest path length. Average Graph Distance This the average length of all shortest paths between the connected pair of nodes of the network. Clustering Coefficient Ctrl+C Calculates and displays the average Clustering Coefficient of the network. Clustering Coefficient The Clustering Coefficient of a vertex quantifies how close the vertex and its neighbors are to being a clique. OutDegree Calculates and displays OutDegree Centralities OutDegree Centrality For each node k, this is the number of arcs starting from it. This is oftenly a measure of activity. InDegree Calculates and displays InDegree Centralities InDegree Centrality For each node k, this the number of arcs ending at k. Most in-degree central node might be considered more prominent among others. Calculates and displays Closeness Centralities Closeness Centrality For each node k, this the invert sum of the shortest distances between k and every other node. It is interpreted as the ability to access information through the "grapevine" of network members. Calculates and displays Betweenness Centralities Betweenness Centrality For each node k, this is the ratio of all geodesics between pairs of nodes which run through k. It reflects how often an node lies on the geodesics between the other nodes of the network. It can be interpreted as a measure of control. Calculates and displays Graph Centralities Graph Centrality For each node k, this is the invert of the maximum of all geodesic distances from k to all other nodes in the network. Nodes with high GC have short distances to all other nodes in the graph. Calculate and display Stress Centrality Stress Centrality For each node k, this is the total number of geodesics between all other nodes which run through k. When one node falls on all other geodesics between all the remaining (N-1) nodes, then we have a star graph with maximum Stress Centrality Calculate and display Eccentricity Centrality Stress Centrality For each node k, this is the largest geodesic distance (k,t) from every other vertex t. Therefore, EC(u) reflects how far, at most, is each node from every other node. Calculate and display Informational Centrality Informational Centrality Calculate and display Informational Centrality Display Num&bers Toggles displaying of node numbers Display Numbers Enables/disables node numbers Display Labels Toggles displaying of node labels Display Labels Enables/disables node labels Display Links Toggle to display or not links Display Links Click to enable or disable displaying of links Display Weight Numbers Toggles displaying of numbers of links weights Display Weight Numbers Click to enable or disable displaying numbers of links weight Display Arrows Toggles displaying of arrows in the end of links Display Arrows Click to enable or disable displaying of arrows in the end of links Thickness=Weight Draws links as thick as their weights (if specified) Draw As Thick As Weights Click to toggle having all links as thick as their weight (if specified) Bezier Curves Draws links as Bezier curves Links Bezier Enables/Disables drawing Links as Bezier curves. Anti-Aliasing F8 Enables/disables anti-aliasing Enable or disable Anti-Aliasing Anti-aliasing is a technique which makes nodes, lines and text, smoother and fancier. But it comes at the cost of speed... Progress Bars F10 Enables/disables Progress Bars Enable or disable Progress Bars Progress Bars may appear during time-cost operations. Enabling progressBar has a significant cpu cost but lets you know about the progress of a given operation. Debug Messages F9 Enables/disables printing debug messages to stdout Enables or disable Debug Messages Printing debug messages to strerr. Enabling has a significant cpu cost but lets you know what SocNetV is actually doing. Toolbar Enables/disables the toolbar Enable or disable Toolbar The toolbar is the widget right below the menu, and carries useful icons. You can disable it if you like... Statusbar Enables/disables the statusbar Enable or disable Statusbar The statusbar is the widget at the bottom of the window, where messages appear. You might want to disable it... Manual F1 Read the manual... Manual Displays the documentation of SocNetV Tip of the Day Read useful tips Quick Tips Displays some useful and quick tips About SocNetV About Basic information about SocNetV About Qt About About Qt &Network Create Random Network Export... &Edit Node... Link... Filter... Colors &Layout In circles by centrality... In levels by centrality... Physical... &Analysis Centralities &Options Nodes Links &View &Help 25% 50% 75% 100% 125% 150% 175% Rotation: &Add Node Add a new node to the network (Ctrl+A). Alternately, you can create a new node in a specific position by double-clicking on that spot of the canvas. &Remove Node Remove a node from the network (Ctrl+Shift+A). Alternately, you can remove a node by right-clicking on it. Add &Link Add a new link to the network (Ctrl+L). Alternately, you can create a new link between two nodes by middle-clicking on them consequetively. Remove Link Remove a link from the network Alternately, you can remove a link by right-clicking on it. Edit Network Edit Total Nodes Total Links Counts how many nodes (vertices) exist in the whole network. Counts how many links (in and out) exist in the whole network. Density The density of a network is the ratio of existing links to all possible links (n(n-1)) between nodes. OutLinked Nodes: This the number of nodes with outEdges to another node. They may also have inEdges or reciprocal links. Meaningful on directed graphs. InLinked Nodes: This the number of nodes with inEdges from another node. These may also have outEdges or reciprocal links. Meaningful on directed graphs. Reciprocal-Linked: This the number of nodes with reciprocal links, namely, both inEdges and outEdges to another node. Active Node Node Number: This is the number of the last selected node. Node In-Degree: This is the number of edges ending at the node you clicked on. Node Out-Degree: This is the number of edges starting from the node you clicked on. Clustering Coef. The Clustering Coefficient quantifies how close the vertex and its neighbors are to being a clique. The proportion of links between the vertices within the neighbourhood of the clicked vertex, divided by the number of links that could possibly exist between them. Network Analysis Embeds a spring-gravitational model on the network, where each node is regarded as physical object reppeling all other nodes, while springs between connected nodes attact them. The result is constant movement. This is a very SLOW process on networks with N > 100! In Fruchterman-Reingold model, the vertices behave as atomic particles or celestial bodies, exerting attractive and repulsive forces to each other. Again, only vertices that are neighbours attract each other but, unlike Spring Embedder, all vertices repel each other. Kamanda-Kwei ! If you enable this, all nodes will be resized so that their size reflect their out-degree (the amount of links from them). To put it simply, more out-linked nodes will be bigger... If you enable this, all nodes will be resized so that their size reflect their in-degree (the amount of links to them from other nodes). To put it simply, more in-linked nodes will be bigger... Layout Ready. Social Network Visualiser Ready Do you want to save the changes to the network file? Yes No Cancel Choose a network file... Select one file to open All (*);;GraphML (*.graphml *.gml);;GraphViz (*.dot);;Adjacency (*.txt *.csv *.net);;Pajek (*.net *.pajek);;DL (*.dl *.net) Loaded network: Error loading requested file. Aborted. Opening aborted Saving file... No network loaded. Do you want to save this network in Pajek-formatted or SocioMatrix - formatted file? Pajek Sociomatrix Network saved under filename: . Saving network under new filename... Saving aborted Closing file... Network has not been saved. Do you want to save before closing it? Erasing old network data.... Printing... Pajek formatted network, named %1, loaded with %2 Nodes and %3 total Links. Adjacency formatted network, named %1, loaded with %2 Nodes and %3 total Links. Dot formatted network, named %1, loaded with %2 Nodes and %3 total Links. GraphML formatted network, named %1, loaded with %2 Nodes and %3 total Links. DL-formatted network, named %1, loaded with %2 Nodes and %3 total Links. New node (numbered %1) added. The canvas is empty! Load a network file or create a new network first. Cannot export PNG. Save Image Files (*.png) Image Saved as: Exporting completed Nothing to export! Load a network file or create a new network first. Cannot export BMP. Save Image as Image Files (*.bmp) Export to BMP... Cannot export PDF. Export to PDF Portable Document Format files (*.pdf) Export to PDF... File saved as: Cannot export to Pajek. Could not write to %1 File %1 saved Nothing to export! Load a network file or create a new network first. Cannot export to Adjacency Matrix. Note that exporting to an adjacency matrix does not save floating-point weight values; adjacency matrices consist of integers, only. If your network had any floating point weights in some edges, these are being truncated to the nearest integer or 1.) Adjacency matrix-formatted network saved into file %1 Cannot export to DL. Cannot export to GW. Viewing network file - Loaded network text file Network not saved yet. I will open a dialog for you to save it now. Network has been modified. Please save it now. Empty network! Load a network file first or create and save a new one... Nothing here. Not my fault, though! Empty network! Load a network file or create something by double-clicking on the canvas! Nothing to show! creating adjacency adjacency matrix of %1 nodes View Adjacency Matrix - This will create a new random symmetric network of G(n,p) model, where n is the nodes and p is the edge probability. Please enter the number n of nodes you want: Creating random network. Please wait... Random network created. Nodes: Edges: Average path length: Clustering coefficient: On the average, edges should be This graph is almost surely connected because: probability > ln(n)/n, that is: bigger than This graph is almost surely not connected because: probability < ln(n)/n, that is: Creating uniform random network. Please wait... This will create a same degree network. Please enter the number of nodes you want: Sorry. I cannot create such a network. Links must be even number This will create a small world network, that is an undirected graph with N nodes and N*d/2 edges, where d is the mean edge degree. Please enter the number N of nodes you want: Now, enter an even number d. This is the mean edge degree each new node will have: Now, enter a parameter beta. This is the edge rewiring probability: Erasing any existing network. Creating small world. Please wait... Creating random network. Please wait (or disable me from Options > View > ProgressBar, next time ). Small world random network created: Small world network created. This will create a ring lattice network, where each node has degree d: d/2 edges to the right and d/2 to the left. Please enter the number of nodes you want: Ring lattice network created. No nodes present! Load a network file first or create some nodes... OK Nothing to find! Enter node label or number: Node found! Sorry. There is no such node in this network. Try again. Options (%1, %2); Node %3, with label %4, has %5 in-Links and %6 out-Links. Link from Node %1 to Node %2, has weight %3 and color %4. Link between node %1 and node %2, weight %3 and color %4. Nothing to do! Load a network file or add some nodes first. Nothing to remove. Choose a node to remove between ( Node removed completely. Ready. Nothing to link to! Create some nodes first. There are no nodes yet... This will draw a new link between two nodes. Enter source node ( Aborting. Source node accepted. Now enter target node ( Source and target nodes accepted. Please, enter the weight of new link: Ready. No links present! Load a network file or create a new network first. No links to remove - sorry. Remove link Source node: ( Target node: ( There is no such link. This link is reciprocal. Select what Direction to delete or Both... Both There are no nodes! Load a network file or create a new network first. No nodes created. Please click on a node first... Enter new node label: Changed label to %1. Ready. No label text. Abort. No nodes... Select node: ( Error. There is no such link. Change node color aborted. Cannot change nothing. Change node size to: (1-16) No links here! Load a network file or create a new network first. No links present... Select link source node: ( Select link target node: ( Change link color cancelled. User abort. There are no links here! Load a network file or create a new network first. New link Weight: input error. Abort. Change link weight Select what Direction to change or Both... New link weight: Change link weight cancelled. Weight not changed. Sorry. Nothing to filter! Load a network file or create a new network. Then ask me to compute something! Nothing to filter! All links are reciprocal. Your network is symmetric... There are node nodes yet! Load a network file or create a new network first. Then we can talk about layouts! I am really sorry. You must really load a file first... Embedding a spring-gravitational model on the network.... Click on the checkbox "Spring-Embedder" to stop movement! Movement stopped! There are no nodes yet! Load a network file or create a new network first. Then we can talk about layouts! Embedding a repelling-attracting forces model on the network.... Click on the checkbox "Fruchterman-Reingold" to stop movement! Wake up! Load a network file or create a new network first. Then we can talk about layouts! Embedding node size model on the network.... You must be dreaming! Load a network file or create a new network first. Then we can talk about layouts! Sorry, I can't follow! Load a network file or create a new network first. Then we can talk about layouts! Nothing to layout! Are you dreaming? Calculating new nodes positions. Please wait... Nodes in inner circles have greater In-Degree Centrality. Load a network file or create a new network first! Nodes in inner circles have greater Out-Degree Centrality. Sorry, there are no nodes yet! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Closeness Centrality. No nodes yet! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Betweenness Centrality. Nothing to do! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Stress Centrality. Nothing to do here! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Graph Centrality. Nodes in inner circles have greater Eccentricity Centrality. Nodes in upper levels have greater In-Degree Centrality. Load a network file or create a new network first. Then we can talk about layouts! Nodes in upper levels have greater Out-Degree Centrality. Nodes in upper levels have greater Closeness Centrality. Nodes in upper levels have greater Betweenness Centrality. There are no nodes! Load a network file or create a new network. Then ask me to compute something! There is no network! Adjacency matrix symmetry = YES Adjacency matrix symmetry = NO There are no nodes. Nothing to do... Distance between two nodes Select source node: ( Select target node: ( Distance calculation operation cancelled. Distance Network distance ( The nodes are connected. The nodes are not connected. There are no nodes nor links! Load a network file or create a new network. Then ask me to compute something! Nothing to do! Creating distance matrix. Please wait... Matrix of sigmas Matrix of distances Cannot find the diameter of nothing... Diameter calculated. Ready. Average distance calculated. Ready. Window resized to (%1, %2) pixels. Nothing to do! Load a network file or create a new network. Then ask me to compute something! No network here. Sorry. Nothing to do. CLUSTERING COEFFICIENT REPORT Created: CLUSTERING COEFFICIENT (CLC) OF EACH NODE CLC range: 0 < C < 1 Range: 0 < GCLC < 1 GCLC = 0, when there are no cliques (i.e. acyclic tree). GCLC = 1, when every node and its neighborhood are complete cliques. Take weights into account (Default: No)? OUT-DEGREE CENTRALITY REPORT OUT-DEGREE CENTRALITIES (ODC) OF EACH NODE ODC range: 0 < C < ODC' range: 0 < C'< 1 GODC range: 0 < GODC < 1 GODC = 0, when all in-degrees are equal (i.e. regular lattice). GODC = 1, when one node completely dominates or overshadows the other nodes. The degree of the node is a measure of the 'activity' of the node it represents Nothing to do! Load a network file or create a new network. Then ask me to compute something! Nothing to do... IN-DEGREE CENTRALITY REPORT IN-DEGREE CENTRALITIES (IDC) OF EACH NODE IDC range: 0 < C < IDC' range: 0 < C'< 1 GIDC range: 0 < GIDC < 1 GIDC = 0, when all in-degrees are equal (i.e. regular lattice). GIDC = 1, when one node is linked from every other node. The in-degree of the node is a measure of the 'activity' of the node it represents CLOSENESS - CENTRALITY REPORT CLOSENESS CENTRALITY (CC) OF EACH NODE CC(u) is the invert sum of the distances of node u from all other nodes. CC' is the standardized CC CC range: 0 < C < CC' range: 0 < C'< 1 All nodes have the same CC value. Node has the maximum ACC value (std): has the minimum ACC value (std): There are different Closeness Centrality classes. GROUP CLOSENESS CENTRALISATION (GCC) GCC = GCC range: 0 < GCC < 1 GCC = 0, when the lengths of the geodesics are all equal (i.e. a complete or a circle graph). GCC = 1, when one node has geodesics of length 1 to all the other nodes, and the other nodes have geodesics of length 2 to the remaining (N-2) nodes. This is exactly the situation realised by a star graph. This measure focuses on how close a node is to all the other nodes in the set of nodes. The idea is that a node is central if it can quickly interact with all others Nothing to do... Please wait... Finished with shortest-path distances... BETWEENESS - CENTRALITY REPORT BETWEENESS CENTRALITY (BC) OF EACH NODE BC of a node u is the sum of delta (s,t,u) for all s,t in V Delta(s,t,u) is the ratio of all geodesics between s and t which run through u. Therefore, BC(u) reflects how often the node u lies on the geodesics between the other nodes of the network BC' is the standardized BC BC range: 0 < BC < (Number of pairs of nodes excluding i) BC' range: 0 < BC'< 1 (C' is 1 when the node falls on all geodesics) All nodes have the same BC value. Node has the maximum BC value: has the minimum BC value: different Betweenness Centrality classes. GROUP BETWEENESS CENTRALISATION (GBC) GBC = GBC range: 0 < GBC < 1 GBC = 0, when all the nodes have exactly the same betweenness index. GBC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. Nothing to do! Why dont you try creating something first? STRESS CENTRALITY REPORT STRESS CENTRALITY (SC) OF EACH NODE SC(u) is the sum of sigma(s,t,u): the number of geodesics from s to t through u. SC(u) reflects the total number of geodesics between all other nodes which run through u SC range: 0 < SC < SC' range: 0 < SC'< 1 (SC'=1 when the node falls on all geodesics) All nodes have the same SC value. has the maximum SC value: has the minimum SC value: different Stress Centrality classes. GROUP STRESS CENTRALISATION (GSC) GSC = GSC range: 0 < GSC < 1 GSC = 0, when all the nodes have exactly the same stress index. GSC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. Try creating a network first. Then I compute whatever you want... GRAPH - CENTRALITY REPORT GRAPH CENTRALITY (GC) OF EACH NODE GC range: 0 < GC < GC' range: 0 < GC'< 1 (GC'=1 => directly linked with all nodes) All nodes have the same GC value. has the maximum GC value: has the minimum GC value: different Graph Centrality classes. GROUP GRAPH CENTRALISATION (GGC) GGC = GGC range: 0 < GGC < 1 GGC = 0, when all the nodes have exactly the same graph index. GGC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. ECCENTRICITY- CENTRALITY REPORT ECCENTRICITY CENTRALITY (EC) OF EACH NODE EC of a node u is the largest geodesic distance (u,t) for t in V Therefore, EC(u) reflects how far, at most, is each node from every other node. EC' is the standardized EC EC range: 0 < EC < (max geodesic distance) EC' range: 0 < EC'< 1 All nodes have the same EC value. has the maximum EC value: has the minimum EC value: different eccentricity Centrality classes. GROUP ECCENTRICITY CENTRALISATION (GEC) GEC = GEC range: 0 < GEC < 1 GEC = 0, when all the nodes have exactly the same betweenness index. GEC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. Eccentricity Centralities saved as: There are no nodes! Load a network file or create a new network. Errr...no nodes here. Sorry! Toggle Nodes Numbers. Please wait... Node Numbers are invisible now. Click the same option again to display them. Node Labels are visible again... There are no nodes! Load a network file or create a new network first. No nodes found. Sorry... Toggle Nodes Labels. Please wait... Node Labels are invisible now. Click the same option again to display them. Select new size for all nodes: (1-16) All shapes have been changed. Ready Change node shapes aborted... Change all nodenumbers size to: (1-16) Change font size: Aborted. Changed numbers size. Ready. There are no links! Load a network file or create a new network first. No nodes or edges found. Sorry... Toggle Edges Weights. Please wait... Edge weights are invisible now. Click the same option again to display them. Edge weights are visible again... There are no nodes nor links! Load a network file or create a new network first! No links found... Toggle Edges Arrows. Please wait... Links are invisible now. Click again the same menu to display them. Links visible again... There are no links! Load a network file or create a new network first! There are no links! Load a network file or create a new network! There are NO links here! Toggle links bezier. Please wait... Change link color aborted. Numbers' colors changed. Ready. Label colors changed. Ready. Toggle anti-aliasing. This will take some time if the network is large (>500)... Anti-aliasing off. Anti-aliasing on. Toggle progressbar... Progress bars off. Progress bars on. Debug messages off. Debug messages on. Toggle toolbar... Toolbar off. Toolbar on. Toggle statusbar... Status bar off. Status bar on. Tip Of The Day You can add a new node by double-clicking on the scene. You can add a new node by clicking on Add button. You can remove a node by clicking on Remove button. You can rotate the network by selecting a new angle on the dock. You can add a new link between two nodes, by middle-clicking (or pressing both mouse buttons simultanesously) on the first and then on the second node. You can remove a node by right-clicking on it and selecting Remove. You can change background color (from the menu Edit > Colors). Nodes can have the colors of your choice. Just right-click on a node and then select > Options > Change Color. You can select every color supported by the X.org palette. The tabs on the left dock show information about the network (nodes, edges, density, etc) as well as information about any node you clicked on (inDegrees, outDegrees, clustering). You can move a node easily by dragging it with your mouse. SocNetV can save the positions of the nodes in a network, if you save it in Pajek/GraphML format. You can apply layout algorithms on the network from the menu Layout or by clicking on the Dock > Layout tab checkboxes You can change the label of node by right-clicking on it, and selecting Options > Change Label. All basic operations of SocNetV are available from the dock on the left, or by right-clicking on a node or a link. Node information is displayed on the Status bar, when you left-click on it. Link information is displayed on the Status bar, when you left-click on it. Manual TextEditor &New Ctrl+N Create a new file &Open... Ctrl+O Open an existing file &Save Ctrl+S Save the document to disk Save &As... Save the document under a new name E&xit Ctrl+Q Exit the application Cu&t Ctrl+X Cut the current selection's contents to the clipboard &Copy Ctrl+C Copy the current selection's contents to the clipboard &Paste Ctrl+V Paste the clipboard's contents into the current selection &About Show the application's About box About &Qt Show the Qt library's About box &File &Edit &Help File Edit Ready TextEditor The document has been modified. Do you want to save your changes? Application Cannot read file %1: %2. File loaded Cannot write file %1: %2. File saved %1[*] - %2 socnetv-2.2/translations/socnetv_es.ts000644 000765 000024 00000604765 13040701535 021306 0ustar00dimitrisstaff000000 000000 HTMLViewer &File &Open Ctrl+O Opens another helpfile &Print Ctrl+P Prints out the actual network E&xit Ctrl+X Close Manual &Back Ctrl+B &Backward &Forward Ctrl+F &Home Ctrl+H &Go MainWindow Welcome to Social Networks Visualiser, Version &New Ctrl+N Creates a new network New network (Ctrl+N) New Creates a new network &Open Ctrl+O Open network (Ctrl+O) Opens a a file of an existing network Open Opens a file of an existing network &Save Ctrl+S Save network (Ctrl+S) Saves the actual network to the current file Save. Saves the actual network Save &As... Ctrl+Shift+S Saves the actual network under a new filename Save As Saves the actual network under a new filename &BMP... Export network to a BMP image Export BMP Export network to a BMP image &PNG... Export network to a PNG image Export PNG Export network to a PNG image &PDF... Export network to a PDF file Export PDF Export network to a PDF document &Adjacency Matrix Export network to an adjacency matrix file Export Sociomatrix Export network to a adjacency matrix-formatted file &Pajek Export network to a Pajek-formatted file Export Pajek Export network to a Pajek-formatted file &List Export network to a List-formatted file. Export List Export network to a List-formatted file &DL... Export network to a DL-formatted file Export DL Export network to a DL-formatted &GW... Export network to a GW-formatted file Export Export network to a GW formatted file &Close Closes the actual network Close Closes the actual network &Print Ctrl+P Prints whatever is viewable on the canvas. Printing This function prints whatever is viewable on the canvas. To print the whole network, you might want to zoom-out. E&xit Ctrl+Q Quits the application Exit Quits the application View Loaded File F5 Displays the loaded network file View Loaded File Displays the file of the loaded network View Adjacency Matrix F6 Displays the adjacency matrix of the active network View Network file Displays the adjacency matrix of the active network Erdos-Renyi G(n,p) Shift+U Creates a random network where each edge is included with a given probability Uniform Creates a random network of G(n, p) model by connecting nodes randomly. Each edge is included in the graph with equal probability p, independently of the other edges Connected Creates a connected random network Uniform Connected Creates a connected random network Ring Lattice Shift+L Creates a ring lattice random network Ring Lattice A ring lattice or a physicist's lattice is a graph with N nodes each connected to K neighbors, K / 2 on each side. Same Degree Creates a random network where all nodes have the same degree. Same Degree Creates a random network where all nodes have the same degree Gaussian Creates a Gaussian distributed random network Gaussian Creates a random network of Gaussian distribution Small World Shift+W Creates a random network with small world properties Small World A Small World, according to the Watts and Strogatz model, is a random network with short average path lengths and high clustering coefficient. Find Node Ctrl+F Finds and highlights a node by number or label. Press Ctrl+F again to undo. Find Node Finds a node with a given number or label and doubles its size. Ctrl+F again resizes back the node Add Node Ctrl+A Adds a node Add Node Adds a node to the network Remove Node Ctrl+Shift+A Removes a node Remove Node Removes a node from the network Change Label Changes the Label of a node Change Label Changes the label of a node Change Color Changes the color of a node Change Color Changes the Color of a node Change Size Changes the actual size of a node Change Size Changes the actual size of a node Change Value Changes the value of a node Change Value Changes the value of a node Change all Nodes Size This option lets you change the size of all nodes Nodes Size This option lets you change the size of all nodes Change all Nodes Shape This option lets you change the shape of all nodes Nodes Shape This option lets you change the shape of all nodes Change Node Shape to Box This option lets you change the shape of a node to a box Node as a box This option lets you change the shape of a node to a box Change Node Shape to Triangle Change Node Shape to Circle Change Node Shape to Diamond Change Node Shape to Ellipse Change all Numbers Size It lets you change the font size of the numbers of all nodes Numbers Size Changes the size of the numbers of all nodes Change all Labels Size You can change the font size of the labels of all nodes Labels Size Change the fontsize of the labels of all nodes Add Link Ctrl+L Adds a Link to a Node Add Link Adds a Link to the network Remove Ctrl+Shift+L Removes a Link Remove Link Removes a Link from the network Changes the Label of a Link Change Label Changes the label of a Link Changes the Color of a Link Change Color Changes the Color of a Link Change Weight Changes the Weight of a Link Change Value Changes the Weight of a Link Filter Nodes Filters Nodes of some value out of the network Filter Nodes Filters Nodes of some value out of the network. Filter Links Filters Links of some weight out of the network Filter Links Filters Link of some specific weight out of the network. Change Background Color Click to change the background color Background Changes background color Change all Nodes Colors Click to choose a new color for all nodes. All Nodes Changes all nodes color at once. Change all Numbers Colors Click to change the color of all numbers. Numbers Changes the color of all numbers. Change all Labels Colors Click to change the color of all node labels. Numbers Changes the color of all node labels. Change all Links Colors Click to change the color of all links. Background Changes all links color Transform Nodes to Links Transforms the network so that nodes become links and vice versa Transform Nodes LinksAct Transforms network so that nodes become links and vice versa Symmetrize Links Shift+R Makes all edges reciprocal (thus, a symmetric graph). Symmetrize Edges Transforms all arcs to double links (edges). The result is a symmetric network Strong Structural Nodes are assigned the same color if they have identical in and out neighborhoods Click this to colorize nodes; Nodes are assigned the same color if they have identical in and out neighborhoods Regular Nodes are assigned the same color if they have neighborhoods of the same set of colors Click this to colorize nodes; Nodes are assigned the same color if they have neighborhoods of the same set of colors Random Repositions the nodes in random places Random Layout Repositions the nodes in random places Random Circle Repositions the nodes randomly on a circle Random Circle Layout Repositions the nodes randomly on a circle In-Degree Ctrl+1 Repositions the nodes on circles of different radius. More In-Degree Central Nodes are positioned towards the centre. Circle In-Degree Centrality Layout Repositions the nodes on circles of different radius. More In-Degree Central Nodes are positioned towards the centre. Out-Degree Ctrl+2 Repositions the nodes on circles of different radius. More Out-Degree Central Nodes are positioned towards the centre. Circle Out-Degree Centrality Layout Repositions the nodes on circles of different radius. More Out-Degree Central Nodes are positioned towards the centre. Closeness Ctrl+3 Repositions the nodes on circles of different radius. More Closeness Central Nodes are positioned towards the centre. Circle Closeness Centrality Layout Repositions the nodes on circles of different radius. More Closeness Central Nodes are positioned towards the centre. Betweenness Ctrl+4 Repositions the nodes on circles of different radius. More Betweenness Central Nodes are positioned towards the centre. Circle Betweenness Centrality Layout Repositions the nodes on circles of different radius. More Betweenness Central Nodes are positioned towards the centre. Informational Ctrl+5 Repositions the nodes on circles of different radius. More Informational Central Nodes are situated towards the centre. Circle Informational Centrality Layout Repositions the nodes on circles of different radius. More Informational Central Nodes are positioned towards the centre. Stress Ctrl+6 Repositions the nodes on circles of different radius. More Stressed Central Nodes are positioned towards the centre. Circle Stress Centrality Layout Repositions the nodes on circles of different radius. Nodes having greater Stress Centrality are situated towards the centre. Graph Ctrl+7 Repositions the nodes on circles of different radius. More Graphed Central Nodes are positioned towards the centre. Circle Graph Centrality Layout Repositions the nodes on circles of different radius. Nodes having greater Graph Centrality are situated towards the centre. Eccentricity Ctrl+8 Repositions the nodes on circles of different radius. Nodes of large eccentricity are positioned towards the centre. Circle Eccentricity Centrality Layout Repositions the nodes on circles of different radius. Nodes having greater Eccentricity Centrality are situated towards the centre. Remove Layout GuideLines Removes Red GuideLines from the canvas. Remove GuideLines Removes any guidelines (circles or horizontal lines) created for the network layout. Ctrl+Shift+1 Repositions the nodes on levels of different height. More In-Degree Central Nodes are situated on higher levels. Level In-Degree Centrality Layout Repositions the nodes on levels of different height. More In-Degree Central Nodes are situated on higher levels. Ctrl+Shift+2 Repositions the nodes on levels of different height. More Out-Degree Central Nodes are situated on higher levels. Level Out-Degree Centrality Layout Repositions the nodes on levels of different height. More Out-Degree Central Nodes are situated on higher levels. Ctrl+Shift+3 Repositions the nodes on levels of different height. More Closeness Central Nodes are situated on higher levels. level Closeness Centrality Layout Repositions the nodes on levels of different height. More Closeness Central Nodes are situated on higher levels. Ctrl+Shift+4 Repositions the nodes on levels of different height. More Betweenness Central Nodes are situated on higher levels. level Betweenness Centrality Layout Repositions the nodes on levels of different height. More Betweenness Central Nodes are situated on higher levels. Ctrl+Shift+5 Repositions the nodes on levels of different height. More Informational Central Nodes are situated on higher levels. Level Informational Centrality Layout Repositions the nodes on levels of different height. More Informational Central Nodes are situated on higher levels. Spring Embedder Alt+1 All nodes repel each other while the connected ones are attracted as if connected by springs. Spring Embedder Layout In this model, nodes are regarded as physical bodies (i.e. electrons) which exert repelling forces to each other, while edges are springs connecting adjacents nodes. Non-adjacent nodes repel each other while connected nodes are The algorithm continues until the system retains an equilibrium state in which all forces cancel each other. Fruchterman-Reingold Alt+2 Repelling forces between all nodes, and attracting forces between adjacent nodes. Fruchterman-Reingold Layout Embeds a layout all nodes according to a model in which repelling forces are used between every pair of nodes, while attracting forces are used only between adjacent nodes. The algorithm continues until the system retains its equilibrium state where all forces cancel each other. Zoom &in Ctrl++ Zoom in (Ctrl++) Zooms inside the actual network. Zoom In. Zooms in. What else did you expect? Zoom &out Ctrl+- Zoom out (Ctrl+-) Zooms out of the actual network. Zoom out. Zooms out. What else did you expect? NodeSize = F (OutDegree) Alt+3 Resizes all nodes according to their out edges. NodeSize = F (OutDegree) Adjusts the size of each node according to their out-edges (OutDegree). The more out-likned a node is, the bigger will appear... NodeSize = F (InDegree) Alt+4 Resizes all nodes according to their in edges. NodeSize = F (InDegree) This method adjusts the size of each node according to their in-edges (InDegree). The more in-linked a node is, the bigger will appear... Network Symmetry Shift+S Tests if the network is symmetric or not Network Symmetry A network is symmetric when all edges are reciprocal, or, in mathematical language, when the adjacency matrix is symmetric. Graph Distance Ctrl+G Calculates the length of the shortest path between two nodes... Graph Distance The graph distance (or geodesic distance) of two nodes is the length (number of edges) of the shortest path between them. Distance &Matrix Ctrl+M Displays the matrix of graph distances between all nodes Distance Matrix A distance matrix is a NxN matrix, where the (i,j) element is the graph distance from node i to node j. Diameter Ctrl+D Calculates and displays the diameter of the network. Diameter The Diameter of a network is the maximum graph distance (maximum shortest path length) between any two nodes of the network. Average Graph Distance Ctrl+B Calculates and displays the average shortest path length. Average Graph Distance This the average length of all shortest paths between the connected pair of nodes of the network. Clustering Coefficient Ctrl+C Calculates and displays the average Clustering Coefficient of the network. Clustering Coefficient The Clustering Coefficient of a vertex quantifies how close the vertex and its neighbors are to being a clique. OutDegree Calculates and displays OutDegree Centralities OutDegree Centrality For each node k, this is the number of arcs starting from it. This is oftenly a measure of activity. InDegree Calculates and displays InDegree Centralities InDegree Centrality For each node k, this the number of arcs ending at k. Most in-degree central node might be considered more prominent among others. Calculates and displays Closeness Centralities Closeness Centrality For each node k, this the invert sum of the shortest distances between k and every other node. It is interpreted as the ability to access information through the "grapevine" of network members. Calculates and displays Betweenness Centralities Betweenness Centrality For each node k, this is the ratio of all geodesics between pairs of nodes which run through k. It reflects how often an node lies on the geodesics between the other nodes of the network. It can be interpreted as a measure of control. Calculates and displays Graph Centralities Graph Centrality For each node k, this is the invert of the maximum of all geodesic distances from k to all other nodes in the network. Nodes with high GC have short distances to all other nodes in the graph. Calculate and display Stress Centrality Stress Centrality For each node k, this is the total number of geodesics between all other nodes which run through k. When one node falls on all other geodesics between all the remaining (N-1) nodes, then we have a star graph with maximum Stress Centrality Calculate and display Eccentricity Centrality Stress Centrality For each node k, this is the largest geodesic distance (k,t) from every other vertex t. Therefore, EC(u) reflects how far, at most, is each node from every other node. Calculate and display Informational Centrality Informational Centrality Calculate and display Informational Centrality Display Num&bers Toggles displaying of node numbers Display Numbers Enables/disables node numbers Display Labels Toggles displaying of node labels Display Labels Enables/disables node labels Display Links Toggle to display or not links Display Links Click to enable or disable displaying of links Display Weight Numbers Toggles displaying of numbers of links weights Display Weight Numbers Click to enable or disable displaying numbers of links weight Display Arrows Toggles displaying of arrows in the end of links Display Arrows Click to enable or disable displaying of arrows in the end of links Thickness=Weight Draws links as thick as their weights (if specified) Draw As Thick As Weights Click to toggle having all links as thick as their weight (if specified) Bezier Curves Draws links as Bezier curves Links Bezier Enables/Disables drawing Links as Bezier curves. Anti-Aliasing F8 Enables/disables anti-aliasing Enable or disable Anti-Aliasing Anti-aliasing is a technique which makes nodes, lines and text, smoother and fancier. But it comes at the cost of speed... Progress Bars F10 Enables/disables Progress Bars Enable or disable Progress Bars Progress Bars may appear during time-cost operations. Enabling progressBar has a significant cpu cost but lets you know about the progress of a given operation. Debug Messages F9 Enables/disables printing debug messages to stdout Enables or disable Debug Messages Printing debug messages to strerr. Enabling has a significant cpu cost but lets you know what SocNetV is actually doing. Toolbar Enables/disables the toolbar Enable or disable Toolbar The toolbar is the widget right below the menu, and carries useful icons. You can disable it if you like... Statusbar Enables/disables the statusbar Enable or disable Statusbar The statusbar is the widget at the bottom of the window, where messages appear. You might want to disable it... Manual F1 Read the manual... Manual Displays the documentation of SocNetV Tip of the Day Read useful tips Quick Tips Displays some useful and quick tips About SocNetV About Basic information about SocNetV About Qt About About Qt &Network Create Random Network Export... &Edit Node... Link... Filter... Colors &Layout In circles by centrality... In levels by centrality... Physical... &Analysis Centralities &Options Nodes Links &View &Help 25% 50% 75% 100% 125% 150% 175% Rotation: &Add Node Add a new node to the network (Ctrl+A). Alternately, you can create a new node in a specific position by double-clicking on that spot of the canvas. &Remove Node Remove a node from the network (Ctrl+Shift+A). Alternately, you can remove a node by right-clicking on it. Add &Link Add a new link to the network (Ctrl+L). Alternately, you can create a new link between two nodes by middle-clicking on them consequetively. Remove Link Remove a link from the network Alternately, you can remove a link by right-clicking on it. Edit Network Edit Total Nodes Total Links Counts how many nodes (vertices) exist in the whole network. Counts how many links (in and out) exist in the whole network. Density The density of a network is the ratio of existing links to all possible links (n(n-1)) between nodes. OutLinked Nodes: This the number of nodes with outEdges to another node. They may also have inEdges or reciprocal links. Meaningful on directed graphs. InLinked Nodes: This the number of nodes with inEdges from another node. These may also have outEdges or reciprocal links. Meaningful on directed graphs. Reciprocal-Linked: This the number of nodes with reciprocal links, namely, both inEdges and outEdges to another node. Active Node Node Number: This is the number of the last selected node. Node In-Degree: This is the number of edges ending at the node you clicked on. Node Out-Degree: This is the number of edges starting from the node you clicked on. Clustering Coef. The Clustering Coefficient quantifies how close the vertex and its neighbors are to being a clique. The proportion of links between the vertices within the neighbourhood of the clicked vertex, divided by the number of links that could possibly exist between them. Network Analysis Embeds a spring-gravitational model on the network, where each node is regarded as physical object reppeling all other nodes, while springs between connected nodes attact them. The result is constant movement. This is a very SLOW process on networks with N > 100! In Fruchterman-Reingold model, the vertices behave as atomic particles or celestial bodies, exerting attractive and repulsive forces to each other. Again, only vertices that are neighbours attract each other but, unlike Spring Embedder, all vertices repel each other. Kamanda-Kwei ! If you enable this, all nodes will be resized so that their size reflect their out-degree (the amount of links from them). To put it simply, more out-linked nodes will be bigger... If you enable this, all nodes will be resized so that their size reflect their in-degree (the amount of links to them from other nodes). To put it simply, more in-linked nodes will be bigger... Layout Ready. Social Network Visualiser Ready Do you want to save the changes to the network file? Yes No Cancel Choose a network file... Select one file to open All (*);;GraphML (*.graphml *.gml);;GraphViz (*.dot);;Adjacency (*.txt *.csv *.net);;Pajek (*.net *.pajek);;DL (*.dl *.net) Loaded network: Error loading requested file. Aborted. Opening aborted Saving file... No network loaded. Do you want to save this network in Pajek-formatted or SocioMatrix - formatted file? Pajek Sociomatrix Network saved under filename: . Saving network under new filename... Saving aborted Closing file... Network has not been saved. Do you want to save before closing it? Erasing old network data.... Printing... Pajek formatted network, named %1, loaded with %2 Nodes and %3 total Links. Adjacency formatted network, named %1, loaded with %2 Nodes and %3 total Links. Dot formatted network, named %1, loaded with %2 Nodes and %3 total Links. GraphML formatted network, named %1, loaded with %2 Nodes and %3 total Links. DL-formatted network, named %1, loaded with %2 Nodes and %3 total Links. New node (numbered %1) added. The canvas is empty! Load a network file or create a new network first. Cannot export PNG. Save Image Files (*.png) Image Saved as: Exporting completed Nothing to export! Load a network file or create a new network first. Cannot export BMP. Save Image as Image Files (*.bmp) Export to BMP... Cannot export PDF. Export to PDF Portable Document Format files (*.pdf) Export to PDF... File saved as: Cannot export to Pajek. Could not write to %1 File %1 saved Nothing to export! Load a network file or create a new network first. Cannot export to Adjacency Matrix. Note that exporting to an adjacency matrix does not save floating-point weight values; adjacency matrices consist of integers, only. If your network had any floating point weights in some edges, these are being truncated to the nearest integer or 1.) Adjacency matrix-formatted network saved into file %1 Cannot export to DL. Cannot export to GW. Viewing network file - Loaded network text file Network not saved yet. I will open a dialog for you to save it now. Network has been modified. Please save it now. Empty network! Load a network file first or create and save a new one... Nothing here. Not my fault, though! Empty network! Load a network file or create something by double-clicking on the canvas! Nothing to show! creating adjacency adjacency matrix of %1 nodes View Adjacency Matrix - This will create a new random symmetric network of G(n,p) model, where n is the nodes and p is the edge probability. Please enter the number n of nodes you want: Creating random network. Please wait... Random network created. Nodes: Edges: Average path length: Clustering coefficient: On the average, edges should be This graph is almost surely connected because: probability > ln(n)/n, that is: bigger than This graph is almost surely not connected because: probability < ln(n)/n, that is: Creating uniform random network. Please wait... This will create a same degree network. Please enter the number of nodes you want: Sorry. I cannot create such a network. Links must be even number This will create a small world network, that is an undirected graph with N nodes and N*d/2 edges, where d is the mean edge degree. Please enter the number N of nodes you want: Now, enter an even number d. This is the mean edge degree each new node will have: Now, enter a parameter beta. This is the edge rewiring probability: Erasing any existing network. Creating small world. Please wait... Creating random network. Please wait (or disable me from Options > View > ProgressBar, next time ). Small world random network created: Small world network created. This will create a ring lattice network, where each node has degree d: d/2 edges to the right and d/2 to the left. Please enter the number of nodes you want: Ring lattice network created. No nodes present! Load a network file first or create some nodes... OK Nothing to find! Enter node label or number: Node found! Sorry. There is no such node in this network. Try again. Options (%1, %2); Node %3, with label %4, has %5 in-Links and %6 out-Links. Link from Node %1 to Node %2, has weight %3 and color %4. Link between node %1 and node %2, weight %3 and color %4. Nothing to do! Load a network file or add some nodes first. Nothing to remove. Choose a node to remove between ( Node removed completely. Ready. Nothing to link to! Create some nodes first. There are no nodes yet... This will draw a new link between two nodes. Enter source node ( Aborting. Source node accepted. Now enter target node ( Source and target nodes accepted. Please, enter the weight of new link: Ready. No links present! Load a network file or create a new network first. No links to remove - sorry. Remove link Source node: ( Target node: ( There is no such link. This link is reciprocal. Select what Direction to delete or Both... Both There are no nodes! Load a network file or create a new network first. No nodes created. Please click on a node first... Enter new node label: Changed label to %1. Ready. No label text. Abort. No nodes... Select node: ( Error. There is no such link. Change node color aborted. Cannot change nothing. Change node size to: (1-16) No links here! Load a network file or create a new network first. No links present... Select link source node: ( Select link target node: ( Change link color cancelled. User abort. There are no links here! Load a network file or create a new network first. New link Weight: input error. Abort. Change link weight Select what Direction to change or Both... New link weight: Change link weight cancelled. Weight not changed. Sorry. Nothing to filter! Load a network file or create a new network. Then ask me to compute something! Nothing to filter! All links are reciprocal. Your network is symmetric... There are node nodes yet! Load a network file or create a new network first. Then we can talk about layouts! I am really sorry. You must really load a file first... Embedding a spring-gravitational model on the network.... Click on the checkbox "Spring-Embedder" to stop movement! Movement stopped! There are no nodes yet! Load a network file or create a new network first. Then we can talk about layouts! Embedding a repelling-attracting forces model on the network.... Click on the checkbox "Fruchterman-Reingold" to stop movement! Wake up! Load a network file or create a new network first. Then we can talk about layouts! Embedding node size model on the network.... You must be dreaming! Load a network file or create a new network first. Then we can talk about layouts! Sorry, I can't follow! Load a network file or create a new network first. Then we can talk about layouts! Nothing to layout! Are you dreaming? Calculating new nodes positions. Please wait... Nodes in inner circles have greater In-Degree Centrality. Load a network file or create a new network first! Nodes in inner circles have greater Out-Degree Centrality. Sorry, there are no nodes yet! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Closeness Centrality. No nodes yet! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Betweenness Centrality. Nothing to do! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Stress Centrality. Nothing to do here! Load a network file or create a new network first. Then we can talk about layouts! Nodes in inner circles have greater Graph Centrality. Nodes in inner circles have greater Eccentricity Centrality. Nodes in upper levels have greater In-Degree Centrality. Load a network file or create a new network first. Then we can talk about layouts! Nodes in upper levels have greater Out-Degree Centrality. Nodes in upper levels have greater Closeness Centrality. Nodes in upper levels have greater Betweenness Centrality. There are no nodes! Load a network file or create a new network. Then ask me to compute something! There is no network! Adjacency matrix symmetry = YES Adjacency matrix symmetry = NO There are no nodes. Nothing to do... Distance between two nodes Select source node: ( Select target node: ( Distance calculation operation cancelled. Distance Network distance ( The nodes are connected. The nodes are not connected. There are no nodes nor links! Load a network file or create a new network. Then ask me to compute something! Nothing to do! Creating distance matrix. Please wait... Matrix of sigmas Matrix of distances Cannot find the diameter of nothing... Diameter calculated. Ready. Average distance calculated. Ready. Window resized to (%1, %2) pixels. Nothing to do! Load a network file or create a new network. Then ask me to compute something! No network here. Sorry. Nothing to do. CLUSTERING COEFFICIENT REPORT Created: CLUSTERING COEFFICIENT (CLC) OF EACH NODE CLC range: 0 < C < 1 Range: 0 < GCLC < 1 GCLC = 0, when there are no cliques (i.e. acyclic tree). GCLC = 1, when every node and its neighborhood are complete cliques. Take weights into account (Default: No)? OUT-DEGREE CENTRALITY REPORT OUT-DEGREE CENTRALITIES (ODC) OF EACH NODE ODC range: 0 < C < ODC' range: 0 < C'< 1 GODC range: 0 < GODC < 1 GODC = 0, when all in-degrees are equal (i.e. regular lattice). GODC = 1, when one node completely dominates or overshadows the other nodes. The degree of the node is a measure of the 'activity' of the node it represents Nothing to do! Load a network file or create a new network. Then ask me to compute something! Nothing to do... IN-DEGREE CENTRALITY REPORT IN-DEGREE CENTRALITIES (IDC) OF EACH NODE IDC range: 0 < C < IDC' range: 0 < C'< 1 GIDC range: 0 < GIDC < 1 GIDC = 0, when all in-degrees are equal (i.e. regular lattice). GIDC = 1, when one node is linked from every other node. The in-degree of the node is a measure of the 'activity' of the node it represents CLOSENESS - CENTRALITY REPORT CLOSENESS CENTRALITY (CC) OF EACH NODE CC(u) is the invert sum of the distances of node u from all other nodes. CC' is the standardized CC CC range: 0 < C < CC' range: 0 < C'< 1 All nodes have the same CC value. Node has the maximum ACC value (std): has the minimum ACC value (std): There are different Closeness Centrality classes. GROUP CLOSENESS CENTRALISATION (GCC) GCC = GCC range: 0 < GCC < 1 GCC = 0, when the lengths of the geodesics are all equal (i.e. a complete or a circle graph). GCC = 1, when one node has geodesics of length 1 to all the other nodes, and the other nodes have geodesics of length 2 to the remaining (N-2) nodes. This is exactly the situation realised by a star graph. This measure focuses on how close a node is to all the other nodes in the set of nodes. The idea is that a node is central if it can quickly interact with all others Nothing to do... Please wait... Finished with shortest-path distances... BETWEENESS - CENTRALITY REPORT BETWEENESS CENTRALITY (BC) OF EACH NODE BC of a node u is the sum of delta (s,t,u) for all s,t in V Delta(s,t,u) is the ratio of all geodesics between s and t which run through u. Therefore, BC(u) reflects how often the node u lies on the geodesics between the other nodes of the network BC' is the standardized BC BC range: 0 < BC < (Number of pairs of nodes excluding i) BC' range: 0 < BC'< 1 (C' is 1 when the node falls on all geodesics) All nodes have the same BC value. Node has the maximum BC value: has the minimum BC value: different Betweenness Centrality classes. GROUP BETWEENESS CENTRALISATION (GBC) GBC = GBC range: 0 < GBC < 1 GBC = 0, when all the nodes have exactly the same betweenness index. GBC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. Nothing to do! Why dont you try creating something first? STRESS CENTRALITY REPORT STRESS CENTRALITY (SC) OF EACH NODE SC(u) is the sum of sigma(s,t,u): the number of geodesics from s to t through u. SC(u) reflects the total number of geodesics between all other nodes which run through u SC range: 0 < SC < SC' range: 0 < SC'< 1 (SC'=1 when the node falls on all geodesics) All nodes have the same SC value. has the maximum SC value: has the minimum SC value: different Stress Centrality classes. GROUP STRESS CENTRALISATION (GSC) GSC = GSC range: 0 < GSC < 1 GSC = 0, when all the nodes have exactly the same stress index. GSC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. Try creating a network first. Then I compute whatever you want... GRAPH - CENTRALITY REPORT GRAPH CENTRALITY (GC) OF EACH NODE GC range: 0 < GC < GC' range: 0 < GC'< 1 (GC'=1 => directly linked with all nodes) All nodes have the same GC value. has the maximum GC value: has the minimum GC value: different Graph Centrality classes. GROUP GRAPH CENTRALISATION (GGC) GGC = GGC range: 0 < GGC < 1 GGC = 0, when all the nodes have exactly the same graph index. GGC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. ECCENTRICITY- CENTRALITY REPORT ECCENTRICITY CENTRALITY (EC) OF EACH NODE EC of a node u is the largest geodesic distance (u,t) for t in V Therefore, EC(u) reflects how far, at most, is each node from every other node. EC' is the standardized EC EC range: 0 < EC < (max geodesic distance) EC' range: 0 < EC'< 1 All nodes have the same EC value. has the maximum EC value: has the minimum EC value: different eccentricity Centrality classes. GROUP ECCENTRICITY CENTRALISATION (GEC) GEC = GEC range: 0 < GEC < 1 GEC = 0, when all the nodes have exactly the same betweenness index. GEC = 1, when one node falls on all other geodesics between all the remaining (N-1) nodes. This is exactly the situation realised by a star graph. Eccentricity Centralities saved as: There are no nodes! Load a network file or create a new network. Errr...no nodes here. Sorry! Toggle Nodes Numbers. Please wait... Node Numbers are invisible now. Click the same option again to display them. Node Labels are visible again... There are no nodes! Load a network file or create a new network first. No nodes found. Sorry... Toggle Nodes Labels. Please wait... Node Labels are invisible now. Click the same option again to display them. Select new size for all nodes: (1-16) All shapes have been changed. Ready Change node shapes aborted... Change all nodenumbers size to: (1-16) Change font size: Aborted. Changed numbers size. Ready. There are no links! Load a network file or create a new network first. No nodes or edges found. Sorry... Toggle Edges Weights. Please wait... Edge weights are invisible now. Click the same option again to display them. Edge weights are visible again... There are no nodes nor links! Load a network file or create a new network first! No links found... Toggle Edges Arrows. Please wait... Links are invisible now. Click again the same menu to display them. Links visible again... There are no links! Load a network file or create a new network first! There are no links! Load a network file or create a new network! There are NO links here! Toggle links bezier. Please wait... Change link color aborted. Numbers' colors changed. Ready. Label colors changed. Ready. Toggle anti-aliasing. This will take some time if the network is large (>500)... Anti-aliasing off. Anti-aliasing on. Toggle progressbar... Progress bars off. Progress bars on. Debug messages off. Debug messages on. Toggle toolbar... Toolbar off. Toolbar on. Toggle statusbar... Status bar off. Status bar on. Tip Of The Day You can add a new node by double-clicking on the scene. You can add a new node by clicking on Add button. You can remove a node by clicking on Remove button. You can rotate the network by selecting a new angle on the dock. You can add a new link between two nodes, by middle-clicking (or pressing both mouse buttons simultanesously) on the first and then on the second node. You can remove a node by right-clicking on it and selecting Remove. You can change background color (from the menu Edit > Colors). Nodes can have the colors of your choice. Just right-click on a node and then select > Options > Change Color. You can select every color supported by the X.org palette. The tabs on the left dock show information about the network (nodes, edges, density, etc) as well as information about any node you clicked on (inDegrees, outDegrees, clustering). You can move a node easily by dragging it with your mouse. SocNetV can save the positions of the nodes in a network, if you save it in Pajek/GraphML format. You can apply layout algorithms on the network from the menu Layout or by clicking on the Dock > Layout tab checkboxes You can change the label of node by right-clicking on it, and selecting Options > Change Label. All basic operations of SocNetV are available from the dock on the left, or by right-clicking on a node or a link. Node information is displayed on the Status bar, when you left-click on it. Link information is displayed on the Status bar, when you left-click on it. Manual TextEditor &New Ctrl+N Create a new file &Open... Ctrl+O Open an existing file &Save Ctrl+S Save the document to disk Save &As... Save the document under a new name E&xit Ctrl+Q Exit the application Cu&t Ctrl+X Cut the current selection's contents to the clipboard &Copy Ctrl+C Copy the current selection's contents to the clipboard &Paste Ctrl+V Paste the clipboard's contents into the current selection &About Show the application's About box About &Qt Show the Qt library's About box &File &Edit &Help File Edit Ready TextEditor The document has been modified. Do you want to save your changes? Application Cannot read file %1: %2. File loaded Cannot write file %1: %2. File saved %1[*] - %2 socnetv-2.2/src/application.qrc000755 000765 000024 00000000401 13040701535 017622 0ustar00dimitrisstaff000000 000000 editcopy.xpm editcut.xpm filenew.xpm fileopen.xpm editpaste.xpm filesave.xpm socnetv-2.2/src/dialogclusteringhierarchical.cpp000644 000765 000024 00000010057 13040734201 023213 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialogclusteringhierarchical.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include "dialogclusteringhierarchical.h" #include #include DialogClusteringHierarchical::DialogClusteringHierarchical (QWidget *parent, QString preselectMatrix) : QDialog (parent) { ui.setupUi(this); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); matrixList << "Adjacency" << "Distances"; measureList << "None, use raw input matrix" << "Jaccard distance" << "Hamming distance" << "Euclidean distance" << "Manhattan distance"; linkageList << "Single-linkage (minimum)" << "Complete-linkage (maximum)" << "Average-linkage (UPGMA)"; ui.matrixSelect -> insertItems( 1, matrixList ); if (preselectMatrix == "Distances") { ui.matrixSelect -> setCurrentIndex(1); } ui.metricSelect ->insertItems(1, measureList); ui.metricSelect -> setCurrentIndex(3); ui.linkageSelect -> insertItems( 1, linkageList ); ui.linkageSelect -> setCurrentIndex(2); ui.diagonalCheckBox -> setChecked(false); ui.diagramCheckBox ->setChecked(true); connect ( ui.matrixSelect, SIGNAL(highlighted(QString)), this, SLOT(matrixChanged(QString)) ); } void DialogClusteringHierarchical::matrixChanged(const QString &matrix) { qDebug()<< "DialogClusteringHierarchical::matrixChanged()" << matrix; } void DialogClusteringHierarchical::gatherData(){ qDebug()<< "DialogClusteringHierarchical: gathering Data!..."; QString matrix = ui.matrixSelect ->currentText(); QString metric= (( ui.metricSelect ->isEnabled() ) ? ui.metricSelect ->currentText() : "-" ); QString linkage = ui.linkageSelect -> currentText(); bool diagonal = ui.diagonalCheckBox -> isChecked(); bool diagram = ui.diagramCheckBox -> isChecked(); qDebug()<< "DialogClusteringHierarchical: user selected: " << matrix << metric << linkage; emit userChoices( matrix, metric, linkage,diagonal, diagram ); } void DialogClusteringHierarchical::on_buttonBox_accepted() { this->gatherData(); this->accept(); } void DialogClusteringHierarchical::on_buttonBox_rejected() { this->reject(); } DialogClusteringHierarchical::~DialogClusteringHierarchical(){ matrixList.clear(); measureList.clear(); linkageList.clear(); } socnetv-2.2/src/dialogclusteringhierarchical.h000644 000765 000024 00000004633 13040734202 022664 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialogclusteringhierarchical.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef DIALOGCLUSTERINGHIERARCHICAL_H #define DIALOGCLUSTERINGHIERARCHICAL_H #include #include "ui_dialogclusteringhierarchical.h" class DialogClusteringHierarchical: public QDialog { Q_OBJECT public: DialogClusteringHierarchical (QWidget *parent = 0, QString preselectMatrix = ""); ~DialogClusteringHierarchical(); public slots: void gatherData(); signals: void userChoices(const QString &matrix, const QString &similarityMeasure, const QString &linkageCriterion, const bool &diagonal, const bool &diagram); private slots: void on_buttonBox_accepted(); void on_buttonBox_rejected(); void matrixChanged(const QString &matrix); private: Ui::DialogClusteringHierarchical ui; QStringList matrixList, measureList, linkageList; }; #endif socnetv-2.2/src/dialogdatasetselect.cpp000755 000765 000024 00000012646 13040734201 021333 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialogdatasetselect.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include "dialogdatasetselect.h" #include #include DialogDataSetSelect::DialogDataSetSelect (QWidget *parent) : QDialog (parent), ui(new Ui::DialogDataSetSelect) { ui->setupUi(this); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); datasets_list << "Krackhardt: High-tech managers (multirelational), 24 actors" << "Padgett: Florentine Families (marital and business relations), 16 actors" << "Zachary: Karate Club (binary & valued ties), 34 actors" << "Bernard: Killworth Fraternity (multirelational), 58 actors" << "Thurman: In the office: Networks and Coalitions, 15 actors" << "Stokman-Ziegler: Corporate Interlocks in Netherlands, 16 actors" << "Stokman-Ziegler: Corporate Interlocks in West Germany, 15 actors" << "Galaskiewicz: CEOs and clubs (affiliation data)" << "Freeman's EIES networks (multirelational, 32 actors)" << "Freeman: EIES network, at time-1, 48 actors" << "Freeman: EIES network, at time-2, 48 actors" << "Freeman: EIES network, number of messages, 48 actors" << "Freeman: The 34 possible graphs with N=5 (as multirelational), 5 actors" << "Mexican Power Network in the 1940s (list format)" << "Knoke: Bureaucracies Information & Money Exchange Network, 10 actors, 2 relationships" << "Stephenson and Zelen (1989): Network of 40 AIDS patients (sex relationship)" << "Stephenson and Zelen (1989): Information Centrality test dataset, 5 actors" << "Wasserman and Faust: star, circle and line graphs of 7 actors (multirelational)" << "Wasserman and Faust: Countries Trade (basic manufactured goods), 24 actors" << "Petersen graph: A non-planar, undirected graph with 10 vertices and 15 edges" << "Herschel graph: The smallest nonhamiltonian polyhedral graph. 11 nodes, 18 edges"; datasets_filenames << "Krackhardt_High-tech_managers.paj" << "Padgett_Florentine_Families.paj" << "Zachary_Karate_Club.dl" << "Bernard_Killworth_Fraternity.dl" << "Thurman_Office_Networks_Coalitions.dl" << "Stokman_Ziegler_Corporate_Interlocks_Netherlands.dl" << "Stokman_Ziegler_Corporate_Interlocks_West_Germany.dl" << "Galaskiewicz_CEOs_and_clubs_affiliation_network_data.2sm" << "Freeman_EIES_networks_32actors.dl" << "Freeman_EIES_network_48actors_Acquaintanceship_at_time-1.dl" << "Freeman_EIES_network_48actors_Acquaintanceship_at_time-2.dl" << "Freeman_EIES_network_48actors_Messages.dl" << "Freeman_34_possible_graphs_with_N_5_multirelational.paj" << "Mexican_Power_Network_1940s.lst" << "Knoke_Bureaucracies_Network.pajek" << "Stephenson&Zelen_40_AIDS_patients_sex_contact.paj" << "Stephenson&Zelen_5actors_6edges_IC_test_dataset.paj" << "Wasserman_Faust_7actors_star_circle_line_graphs.paj" << "Wasserman_Faust_Countries_Trade_Data_Basic_Manufactured_Goods.pajek" << "Petersen_Graph.paj" << "Herschel_Graph.paj"; (ui->selectBox) -> insertItems( 1, datasets_list ); } void DialogDataSetSelect::gatherData(){ qDebug()<< "DialogDataSetSelect: gathering Data!..."; int index = (ui->selectBox) -> currentIndex(); QString dataset_name = datasets_filenames[index]; qDebug()<< "DialogDataSetSelect: user selected: " << dataset_name; emit userChoices( dataset_name ); } void DialogDataSetSelect::on_buttonBox_accepted() { this->gatherData(); this->accept(); } void DialogDataSetSelect::on_buttonBox_rejected() { this->reject(); } DialogDataSetSelect::~DialogDataSetSelect(){ datasets_list.clear(); datasets_filenames.clear(); } socnetv-2.2/src/dialogdatasetselect.h000755 000765 000024 00000004062 13040734202 020772 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialogdatasetselect.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef DIALOGDATASETSELECT_H #define DIALOGDATASETSELECT_H #include #include "ui_dialogdatasetselect.h" class DialogDataSetSelect: public QDialog { Q_OBJECT public: DialogDataSetSelect (QWidget *parent = 0); ~DialogDataSetSelect(); public slots: void gatherData(); signals: void userChoices(QString); private slots: void on_buttonBox_accepted(); void on_buttonBox_rejected(); private: Ui::DialogDataSetSelect *ui; QStringList datasets_list, datasets_filenames; }; #endif socnetv-2.2/src/dialogdissimilarities.cpp000644 000765 000024 00000006021 13040734201 021667 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialogdissimilarities.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include "dialogdissimilarities.h" #include #include DialogDissimilarities::DialogDissimilarities (QWidget *parent) : QDialog (parent) { ui.setupUi(this); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); variablesLocationList << "Rows" << "Columns" << "Both"; metricList << tr("Euclidean distance") << tr("Manhattan distance") << tr("Hamming distance") << tr("Jaccard distance") << tr("Chebyshev distance"); (ui.variablesLocationSelect) -> insertItems( 1, variablesLocationList ); (ui.metricSelect) -> insertItems( 1, metricList ); (ui.diagonalCheckBox)->setChecked(false); } void DialogDissimilarities::gatherData(){ qDebug()<< "DialogDissimilarities: gathering Data!..."; QString varLocation = (ui.variablesLocationSelect) ->currentText(); QString metric = (ui.metricSelect)->currentText(); bool diagonal = (ui.diagonalCheckBox)->isChecked(); qDebug()<< "DialogDissimilarities: user selected: " << varLocation << metric; emit userChoices( metric, varLocation, diagonal ); } void DialogDissimilarities::on_buttonBox_accepted() { this->gatherData(); this->accept(); } void DialogDissimilarities::on_buttonBox_rejected() { this->reject(); } DialogDissimilarities::~DialogDissimilarities(){ metricList.clear(); variablesLocationList.clear(); } socnetv-2.2/src/dialogdissimilarities.h000644 000765 000024 00000004255 13040734202 021344 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialogdissimilarities.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef DIALOGDISSIMILARITIES_H #define DIALOGDISSIMILARITIES_H #include #include "ui_dialogdissimilarities.h" class DialogDissimilarities: public QDialog { Q_OBJECT public: DialogDissimilarities (QWidget *parent = 0); ~DialogDissimilarities(); public slots: void gatherData(); signals: void userChoices(const QString &metric, const QString &varLocation, const bool &diagonal); private slots: void on_buttonBox_accepted(); void on_buttonBox_rejected(); private: Ui::DialogDissimilarities ui; QStringList variablesLocationList, metricList; }; #endif socnetv-2.2/src/dialogfilteredgesbyweight.cpp000755 000765 000024 00000005001 13040734201 022531 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialogfilteredgesbyweight.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include "dialogfilteredgesbyweight.h" #include #include DialogFilterEdgesByWeight::DialogFilterEdgesByWeight (QWidget *parent) : QDialog (parent) { ui.setupUi(this); connect ( ui.buttonBox,SIGNAL(accepted()), this, SLOT(gatherData()) ); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); (ui.overThresholdBt)-> setChecked(true); } void DialogFilterEdgesByWeight::gatherData(){ qDebug()<< "Dialog: gathering Data!..."; bool overThreshold=false; float my_threshold = static_cast ( (ui.weightThreshold)->value() ); if ( ui.overThresholdBt -> isChecked() ) { qDebug()<< "Dialog: We will filter edges weighted more than threshold: " << my_threshold; overThreshold = true; } else { qDebug()<< "Dialog: We will filter edges weighted less than threshold: " << my_threshold; overThreshold = false; } qDebug()<< "Dialog: emitting userChoices" ; emit userChoices( my_threshold, overThreshold ); } socnetv-2.2/src/dialogfilteredgesbyweight.h000755 000765 000024 00000003700 13040734202 022203 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialogfilteredgesbyweight.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef DIALOGFILTEREDGESBYWEIGHT_H #define DIALOGFILTEREDGESBYWEIGHT_H #include #include "ui_dialogfilteredgesbyweight.h" class DialogFilterEdgesByWeight : public QDialog { Q_OBJECT public: DialogFilterEdgesByWeight (QWidget *parent = 0); public slots: void gatherData (); signals: void userChoices( float, bool); private: Ui::DialogFilterEdgesByWeight ui; }; #endif socnetv-2.2/src/dialognodeedit.cpp000644 000765 000024 00000012261 13040734201 020267 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialognodeedit.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include #include #include #include #include #include #include #include #include #include "dialognodeedit.h" DialogNodeEdit::DialogNodeEdit(QWidget *parent, const QString &l, const int &s, const QColor &col, const QString &sh) : QDialog(parent), ui(new Ui::DialogNodeEdit) { ui->setupUi(this); nodeSize = s; nodeColor = col; nodeShape = sh; nodeLabel = l; ui->labelEdit->setText(nodeLabel); ui->sizeSpin->setValue(nodeSize); if ( nodeShape == "box" ){ ui->boxRadio->setChecked (true); } else if ( nodeShape == "circle" ){ ui->circleRadio->setChecked (true); } else if ( nodeShape == "diamond" ){ ui->diamondRadio->setChecked (true); } else if ( nodeShape == "ellipse" ){ ui->ellipseRadio->setChecked (true); } else if ( nodeShape == "triangle" ){ ui->triangleRadio->setChecked (true); } pixmap = QPixmap(60,20) ; pixmap.fill(nodeColor); ui->colorButton->setIcon(QIcon(pixmap)); connect ( ui->buttonBox,SIGNAL(accepted()), this, SLOT(gatherData()) ); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); (ui->labelEdit)->setFocus(); connect (ui->labelEdit, &QLineEdit::editingFinished, this, &DialogNodeEdit::checkErrors); connect (ui->colorButton, &QToolButton::clicked, this, &DialogNodeEdit::selectColor); } void DialogNodeEdit::gatherData(){ qDebug()<< " DialogNodeEdit::gatherData()" ; nodeLabel = ui->labelEdit->text(); nodeSize = ui->sizeSpin->value(); nodeValue = ui->valueEdit->text(); nodeShape = "circle"; if ( ui->boxRadio->isChecked () ){ nodeShape = "box"; } else if ( ui->circleRadio->isChecked() ){ nodeShape = "circle"; } else if ( ui->diamondRadio->isChecked() ){ nodeShape = "diamond"; } else if ( ui->ellipseRadio->isChecked() ){ nodeShape = "ellipse"; } else if ( ui->triangleRadio->isChecked() ){ nodeShape = "triangle"; } else if ( ui->starRadio->isChecked() ){ nodeShape = "star"; } else { nodeShape = "box"; } emit userChoices(nodeLabel,nodeSize,nodeValue,nodeColor,nodeShape); } void DialogNodeEdit::checkErrors() { qDebug()<< " DialogNodeEdit::checkErrors()" ; QString userLabel = ui->labelEdit->text(); userLabel = userLabel.simplified(); ui->labelEdit->setText(userLabel); if ( ui->labelEdit->text().isEmpty() ) { qDebug() << "empty label!"; QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; effect->setColor(QColor("red")); ui->labelEdit->setGraphicsEffect(effect); //(ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(false); } else { ui->labelEdit->setGraphicsEffect(0); (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(true); } //gatherData(); } void DialogNodeEdit::selectColor() { qDebug()<< " DialogNodeEdit::selectColor()" ; nodeColor = QColorDialog::getColor( Qt::red, this, tr("Select node color") ); if ( nodeColor.isValid()) { qDebug() << " color selected " << nodeColor.name(); pixmap.fill(nodeColor); ui->colorButton->setIcon(QIcon(pixmap)); } else { // user pressed Cancel qDebug() << " Aborted node color"; } } DialogNodeEdit::~DialogNodeEdit() { delete ui; } socnetv-2.2/src/dialognodeedit.h000644 000765 000024 00000004603 13040734202 017736 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialognodeedit.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef DIALOGNODEEDIT_H #define DIALOGNODEEDIT_H #include #include "ui_dialognodeedit.h" class DialogNodeEdit : public QDialog { Q_OBJECT public: explicit DialogNodeEdit(QWidget *parent = 0, const QString &l = "", const int &s = 8, const QColor &c= QColor("red"), const QString &sh = "circle"); ~DialogNodeEdit(); public slots: void checkErrors (); void gatherData (); void selectColor(); signals: void userChoices( const QString, const int, const QString, const QColor, const QString); void nodeEditDialogError(QString); private: QColor nodeColor; QString nodeShape; QString nodeValue; QString nodeLabel; int nodeSize; QPixmap pixmap; Ui::DialogNodeEdit *ui; }; #endif socnetv-2.2/src/dialogpreviewfile.cpp000644 000765 000024 00000010512 13040734201 021012 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialogpreviewfile.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org comment : code borrowed from Qt5 codecs example ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include #include "dialogpreviewfile.h" DialogPreviewFile::DialogPreviewFile(QWidget *parent) : QDialog(parent) { encodingComboBox = new QComboBox; encodingLabel = new QLabel(tr("&Encoding:")); encodingLabel->setBuddy(encodingComboBox); textEdit = new QTextEdit; textEdit->setToolTip(tr("In this area you can preview your file.\n") + (" Select the correct encoding from the menu.\n") + (" Mac and Linux users select UTF-8\n") + (" Windows users select Windows-1253 or UTF-8\n") + (" Windows users in Russia select KOI8-R\n")); textEdit->setLineWrapMode(QTextEdit::NoWrap); textEdit->setReadOnly(true); buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(encodingComboBox, SIGNAL(activated(int)), this, SLOT(updateTextEdit())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); QGridLayout *mainLayout = new QGridLayout; mainLayout->addWidget(encodingLabel, 0, 0); mainLayout->addWidget(encodingComboBox, 0, 1); mainLayout->addWidget(textEdit, 1, 0, 1, 2); mainLayout->addWidget(buttonBox, 2, 0, 1, 2); setLayout(mainLayout); setWindowTitle(tr("Preview file & Choose Encoding")); resize(600, 400); } void DialogPreviewFile::setCodecList(const QList &list) { encodingComboBox->clear(); foreach (QTextCodec *codec, list) encodingComboBox->addItem(codec->name(), codec->mibEnum()); } void DialogPreviewFile::setEncodedData(const QByteArray &data, const QString m_fileName, const int m_format) { fileName = m_fileName; format = m_format; encodedData = data; updateTextEdit(); } void DialogPreviewFile::updateTextEdit() { int mib = encodingComboBox->itemData( encodingComboBox->currentIndex()).toInt(); QTextCodec *codec = QTextCodec::codecForMib(mib); qDebug () << " DialogPreviewFile::updateTextEdit() " << codec->name(); QTextStream in(&encodedData); in.setAutoDetectUnicode(false); in.setCodec(codec); decodedStr = in.readAll(); textEdit->setPlainText(decodedStr); } void DialogPreviewFile::accept() { int mib = encodingComboBox->itemData( encodingComboBox->currentIndex()).toInt(); QTextCodec *codec = QTextCodec::codecForMib(mib); qDebug () << " DialogPreviewFile::accept() returning codec name " << codec->name(); emit loadNetworkFileWithCodec(fileName, codec->name(), format); QDialog::accept(); } socnetv-2.2/src/dialogpreviewfile.h000644 000765 000024 00000004577 13040734202 020476 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialogpreviewfile.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef DIALOGPREVIEWFILE_H #define DIALOGPREVIEWFILE_H #include #include class QComboBox; class QDialogButtonBox; class QLabel; class QTextCodec; class QTextEdit; class DialogPreviewFile : public QDialog { Q_OBJECT public: explicit DialogPreviewFile(QWidget *parent = 0); void setCodecList(const QList &list); void setEncodedData(const QByteArray &data, const QString, const int ); QString decodedString() const { return decodedStr; } signals: void loadNetworkFileWithCodec(const QString, const QString, const int); private slots: void updateTextEdit(); void accept(); private: QByteArray encodedData; QString decodedStr, fileName; int format; QComboBox *encodingComboBox; QLabel *encodingLabel; QTextEdit *textEdit; QDialogButtonBox *buttonBox; }; #endif socnetv-2.2/src/dialogranderdosrenyi.cpp000644 000765 000024 00000013213 13040734201 021522 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialogranderdosrenyi.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include #include #include #include #include #include #include "dialogranderdosrenyi.h" DialogRandErdosRenyi::DialogRandErdosRenyi(QWidget *parent, const float eprob) : QDialog(parent) { qDebug() << "::DialogRandErdosRenyi() " ; ui.setupUi(this); nodes = 0; model = ""; edges = 0; ui.probDoubleSpinBox->setValue(eprob); mode = ""; diag = false; connect ( ui.buttonBox, &QDialogButtonBox::accepted, this, &DialogRandErdosRenyi::gatherData ); ui.buttonBox -> button (QDialogButtonBox::Ok) -> setDefault(true); (ui.nodesSpinBox )->setFocus(); connect (ui.gnpRadioButton, &QRadioButton::clicked, this, &DialogRandErdosRenyi::checkErrors); connect (ui.gnmRadioButton, &QRadioButton::clicked, this, &DialogRandErdosRenyi::checkErrors); ui.gnpRadioButton->setChecked(true); ui.probDoubleSpinBox->setEnabled(true); ui.edgesSpinBox-> setDisabled(true); ui.undirectedRadioButton->setChecked(true); ui.diagCheckBox ->setChecked(false); connect (ui.gnpRadioButton, &QRadioButton::clicked, this, &DialogRandErdosRenyi::gnpModel ); connect (ui.gnmRadioButton, &QRadioButton::clicked, this, &DialogRandErdosRenyi::gnmModel ); connect ( ui.undirectedRadioButton,&QRadioButton::clicked, this, &DialogRandErdosRenyi::setModeUndirected ); connect ( ui.directedRadioButton,&QRadioButton::clicked, this, &DialogRandErdosRenyi::setModeDirected ); connect ( ui.diagCheckBox,&QCheckBox::clicked, this, &DialogRandErdosRenyi::setDiag); } void DialogRandErdosRenyi::gnpModel (){ ui.gnmRadioButton -> setChecked(false); ui.probDoubleSpinBox -> setEnabled(true); ui.edgesSpinBox-> setDisabled(true); } void DialogRandErdosRenyi::gnmModel (){ ui.gnpRadioButton -> setChecked(false); ui.probDoubleSpinBox -> setDisabled(true); ui.edgesSpinBox-> setEnabled(true); } void DialogRandErdosRenyi::setModeDirected (){ ui.directedRadioButton->setChecked(true) ; ui.undirectedRadioButton->setChecked(false) ; } void DialogRandErdosRenyi::setModeUndirected (){ ui.directedRadioButton->setChecked(false) ; ui.undirectedRadioButton->setChecked(true) ; } void DialogRandErdosRenyi::setDiag (){ if (ui.diagCheckBox -> isChecked()) ui.diagCheckBox->setText("Yes, allow"); else ui.diagCheckBox->setText("No, set zero"); } void DialogRandErdosRenyi::checkErrors() { qDebug()<< " DialogRandErdosRenyi::checkErrors()" ; if ( !ui.gnpRadioButton->isChecked() && !ui.gnmRadioButton->isChecked()) { QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; effect->setColor(QColor("red")); QGraphicsColorizeEffect *effect2 = new QGraphicsColorizeEffect; effect2->setColor(QColor("red")); ui.gnpRadioButton->setGraphicsEffect(effect); ui.gnmRadioButton->setGraphicsEffect(effect2); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(false); return; } else { ui.gnpRadioButton->setGraphicsEffect(0); ui.gnmRadioButton->setGraphicsEffect(0); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(true); } } void DialogRandErdosRenyi::gatherData() { qDebug() << "DialogRandErdosRenyi::gatherData() " ; nodes = ui.nodesSpinBox->value(); model = ( ui.gnpRadioButton->isChecked() ) ? "G(n,p)" : "G(n,M)"; if ( ui.gnpRadioButton->isChecked() ) { // eprob = ui.probDoubleSpinBox->value(); } else { edges = ui.edgesSpinBox->value(); } mode = (ui.directedRadioButton->isChecked() ? "digraph" : "graph" ); diag = (ui.diagCheckBox -> isChecked() ? true : false); qDebug() << "nodes " << nodes ; qDebug() << "model " << model; qDebug() << "eprob " << ui.probDoubleSpinBox->value(); qDebug() << "edges " << edges; qDebug() << "mode " << mode; qDebug() << "diag " << diag; emit userChoices(nodes, model, edges, ui.probDoubleSpinBox->value(), mode, diag); } socnetv-2.2/src/dialogranderdosrenyi.h000644 000765 000024 00000004550 13040734202 021174 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialogranderdosrenyi.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef DIALOGRANDERDOSRENYI_H #define DIALOGRANDERDOSRENYI_H #include #include "ui_dialogranderdosrenyi.h" class DialogRandErdosRenyi : public QDialog { Q_OBJECT public: explicit DialogRandErdosRenyi(QWidget *parent=0, const float eprob = 0); public slots: void checkErrors(); void gatherData(); void gnmModel(); void gnpModel(); void setModeDirected(); void setModeUndirected(); void setDiag(); signals: void userChoices( const int nodes, const QString model, const int edges, const float eprob, const QString mode, const bool diag); private: QString model; QString mode; int nodes, edges; bool diag; Ui::DialogRandErdosRenyi ui; }; #endif socnetv-2.2/src/dialograndregular.cpp000644 000765 000024 00000011617 13040734201 021006 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialograndregular.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include #include #include #include #include #include #include #include "dialograndregular.h" DialogRandRegular::DialogRandRegular(QWidget *parent) : QDialog(parent) { qDebug() << "::DialogRandRegular() " ; ui.setupUi(this); nodes = 100; degree = 2; mode = "undirected"; diag = false; connect ( ui.buttonBox, &QDialogButtonBox::accepted, this, &DialogRandRegular::gatherData ); ui.buttonBox -> button (QDialogButtonBox::Ok) -> setDefault(true); ui.degreeSpinBox-> setEnabled(true); ui.undirectedRadioButton->setChecked(true); ui.diagCheckBox ->setChecked(false); ui.diagCheckBox -> setEnabled(false); connect ( ui.undirectedRadioButton,&QRadioButton::clicked, this, &DialogRandRegular::setModeUndirected ); connect ( ui.directedRadioButton,&QRadioButton::clicked, this, &DialogRandRegular::setModeDirected ); connect ( ui.diagCheckBox,&QCheckBox::clicked, this, &DialogRandRegular::setDiag); ui.nodesSpinBox->setFocus(); ui.nodesSpinBox->setValue(nodes); ui.degreeSpinBox->setValue( degree ); connect(ui.nodesSpinBox, SIGNAL(valueChanged(int)), this, SLOT(checkErrors(int))); connect(ui.degreeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(checkErrors(int))); } void DialogRandRegular::modifyDegree(int value) { ui.degreeSpinBox->setValue( qCeil ( qLn (value) )); ui.degreeSpinBox->setMaximum( value ); } void DialogRandRegular::setModeDirected (){ ui.directedRadioButton->setChecked(true) ; ui.undirectedRadioButton->setChecked(false) ; ui.degreeLabel->setText("inDegree=outDegree d"); } void DialogRandRegular::setModeUndirected (){ ui.directedRadioButton->setChecked(false) ; ui.undirectedRadioButton->setChecked(true) ; ui.degreeLabel->setText("Degree d"); } void DialogRandRegular::setDiag (){ if (ui.diagCheckBox -> isChecked()) ui.diagCheckBox->setText("Yes, allow"); else ui.diagCheckBox->setText("No, set zero"); } void DialogRandRegular::checkErrors(const int &i) { Q_UNUSED(i); qDebug()<< " DialogRandRegular::checkErrors()" ; if ( ( ui.degreeSpinBox->value() * ui.nodesSpinBox->value() ) % 2 !=0 ) { QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; effect->setColor(QColor("red")); ui.degreeSpinBox->setGraphicsEffect(effect); ui.nodesSpinBox->setGraphicsEffect(effect); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(false); } else { ui.degreeSpinBox->setGraphicsEffect(0); ui.nodesSpinBox->setGraphicsEffect(0); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(true); } } void DialogRandRegular::gatherData() { qDebug() << "DialogRandRegular::gatherData() " ; nodes = ui.nodesSpinBox->value(); degree= ui.degreeSpinBox->value(); mode = (ui.directedRadioButton->isChecked() ? "digraph" : "graph" ); diag = (ui.diagCheckBox -> isChecked() ? true : false); qDebug() << "nodes " << nodes ; qDebug() << "degree" << degree; qDebug() << "mode " << mode; qDebug() << "diag " << diag; emit userChoices(nodes, degree, mode, diag); } socnetv-2.2/src/dialograndregular.h000644 000765 000024 00000004517 13040734202 020455 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialograndregular.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef DIALOGRANDREGULAR_H #define DIALOGRANDREGULAR_H #include #include "ui_dialograndregular.h" class DialogRandRegular : public QDialog { Q_OBJECT public: explicit DialogRandRegular(QWidget *parent = 0); public slots: void checkErrors(const int &i); void gatherData(); void setModeDirected(); void setModeUndirected(); void setDiag(); void modifyDegree(int value); signals: void userChoices( const int nodes, const int degree, const QString mode, const bool diag); private: QString mode; int nodes, degree; bool diag; Ui::DialogRandRegular ui; }; #endif socnetv-2.2/src/dialograndscalefree.cpp000644 000765 000024 00000011263 13040734201 021273 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt randscalefreeddialog.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include "dialograndscalefree.h" #include #include #include #include #include DialogRandScaleFree::DialogRandScaleFree(QWidget *parent) : QDialog(parent) { qDebug() << "DialogRandScaleFree::DialogRandScaleFree() " ; ui.setupUi(this); nodes = 0; initialNodes = 0; mode = ""; diag = false; connect ( ui.buttonBox, &QDialogButtonBox::accepted, this, &DialogRandScaleFree::gatherData ); ui.buttonBox -> button (QDialogButtonBox::Ok) -> setDefault(true); (ui.nodesSpinBox )->setFocus(); ui.initialNodesSpinBox-> setEnabled(true); ui.undirectedRadioButton->setChecked(false); ui.directedRadioButton->setEnabled(true); ui.directedRadioButton->setChecked(true); ui.diagCheckBox->setText("No, set zero"); ui.diagCheckBox ->setChecked(false); ui.diagCheckBox -> setEnabled(false); connect ( ui.undirectedRadioButton,&QRadioButton::clicked, this, &DialogRandScaleFree::setModeUndirected ); connect ( ui.directedRadioButton,&QRadioButton::clicked, this, &DialogRandScaleFree::setModeDirected ); connect ( ui.diagCheckBox,&QCheckBox::clicked, this, &DialogRandScaleFree::setDiag); } void DialogRandScaleFree::setModeDirected (){ ui.directedRadioButton->setChecked(true) ; ui.undirectedRadioButton->setChecked(false) ; } void DialogRandScaleFree::setModeUndirected (){ ui.directedRadioButton->setChecked(false) ; ui.undirectedRadioButton->setChecked(true) ; } void DialogRandScaleFree::setDiag (){ if (ui.diagCheckBox -> isChecked()) ui.diagCheckBox->setText("Yes, allow"); else ui.diagCheckBox->setText("No, set zero"); } void DialogRandScaleFree::checkErrors() { qDebug()<< " DialogRandSmallWorld::checkErrors()" ; // if ( !ui.gnpRadioButton->isChecked() && !ui.gnmRadioButton->isChecked()) // { // QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; // effect->setColor(QColor("red")); // ui.gnpRadioButton->setGraphicsEffect(effect); // (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(false); // } // else { // ui.gnpRadioButton->setGraphicsEffect(0); // ui.gnmRadioButton->setGraphicsEffect(0); // (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(true); // } //gatherData(); } void DialogRandScaleFree::gatherData() { qDebug() << "DialogRandScaleFree::gatherData() " ; nodes = ui.nodesSpinBox->value(); power = ui.powerSpinBox->value(); initialNodes = ui.initialNodesSpinBox->value(); edgesPerStep = ui.edgesPerStepSpinBox ->value(); zeroAppeal = ui.zeroAppealSpinBox->value(); mode = (ui.directedRadioButton->isChecked() ? "digraph" : "graph" ); // diag = (ui.diagCheckBox -> isChecked() ? true : false); qDebug() << "nodes " << nodes ; qDebug() << "initialNodes " << initialNodes; qDebug() << "mode " << mode; qDebug() << "diag " << diag; emit userChoices(nodes, power, initialNodes, edgesPerStep,zeroAppeal, mode); } socnetv-2.2/src/dialograndscalefree.h000644 000765 000024 00000004602 13040734202 020740 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt randscalefreeddialog.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef DIALOGRANDSCALEFREE_H #define DIALOGRANDSCALEFREE_H #include #include "ui_dialograndscalefree.h" class DialogRandScaleFree : public QDialog { Q_OBJECT public: explicit DialogRandScaleFree(QWidget *parent = 0); public slots: void checkErrors(); void gatherData(); void setModeDirected(); void setModeUndirected(); void setDiag(); signals: void userChoices( const int &nodes, const int &power, const int &initialNodes, const int &edgesPerStep, const float &zeroAppeal, const QString &mode); private: QString mode; int nodes; // n int initialNodes; // m0 int edgesPerStep; //m int power; float zeroAppeal; // a bool diag; Ui::DialogRandScaleFree ui; }; #endif socnetv-2.2/src/dialograndsmallworld.cpp000644 000765 000024 00000012005 13040734201 021515 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialograndsmallworld.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include #include #include #include #include #include #include #include "dialograndsmallworld.h" DialogRandSmallWorld::DialogRandSmallWorld(QWidget *parent) : QDialog(parent), ui(new Ui::DialogRandSmallWorld) { qDebug() << "DialogRandSmallWorld::DialogRandSmallWorld() " ; ui->setupUi(this); nodes = 100; degree = qCeil ( qLn (nodes) ); bprob = 0; mode = "undirected"; diag = false; connect ( ui->buttonBox, &QDialogButtonBox::accepted, this, &DialogRandSmallWorld::gatherData ); ui->buttonBox -> button (QDialogButtonBox::Ok) -> setDefault(true); ui->probDoubleSpinBox->setEnabled(true); ui->degreeSpinBox-> setEnabled(true); ui->undirectedRadioButton->setChecked(true); ui->directedRadioButton->setEnabled(false); ui->diagCheckBox ->setChecked(false); ui->diagCheckBox -> setEnabled(false); connect ( ui->undirectedRadioButton,&QRadioButton::clicked, this, &DialogRandSmallWorld::setModeUndirected ); connect ( ui->directedRadioButton,&QRadioButton::clicked, this, &DialogRandSmallWorld::setModeDirected ); connect ( ui->diagCheckBox,&QCheckBox::clicked, this, &DialogRandSmallWorld::setDiag); connect(ui->nodesSpinBox, SIGNAL(valueChanged(int)), this, SLOT(modifyDegree(int))); ui->nodesSpinBox->setFocus(); ui->nodesSpinBox->setValue(nodes); ui->degreeSpinBox->setValue( degree ); } void DialogRandSmallWorld::modifyDegree(int value) { ui->degreeSpinBox->setValue( qCeil ( qLn (value) )); ui->degreeSpinBox->setMaximum( value ); } void DialogRandSmallWorld::setModeDirected (){ ui->directedRadioButton->setChecked(true) ; ui->undirectedRadioButton->setChecked(false) ; } void DialogRandSmallWorld::setModeUndirected (){ ui->directedRadioButton->setChecked(false) ; ui->undirectedRadioButton->setChecked(true) ; } void DialogRandSmallWorld::setDiag (){ if (ui->diagCheckBox -> isChecked()) ui->diagCheckBox->setText("Yes, allow"); else ui->diagCheckBox->setText("No, set zero"); } void DialogRandSmallWorld::checkErrors() { qDebug()<< " DialogRandSmallWorld::checkErrors()" ; // if ( !ui->gnpRadioButton->isChecked() && !ui->gnmRadioButton->isChecked()) // { // QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect; // effect->setColor(QColor("red")); // ui->gnpRadioButton->setGraphicsEffect(effect); // (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(false); // } // else { // ui->gnpRadioButton->setGraphicsEffect(0); // ui->gnmRadioButton->setGraphicsEffect(0); // (ui->buttonBox) -> button (QDialogButtonBox::Ok) -> setEnabled(true); // } //gatherData(); } void DialogRandSmallWorld::gatherData() { qDebug() << "DialogRandSmallWorld::gatherData() " ; nodes = ui->nodesSpinBox->value(); bprob = ui->probDoubleSpinBox->value(); degree= ui->degreeSpinBox->value(); mode = (ui->directedRadioButton->isChecked() ? "digraph" : "graph" ); diag = (ui->diagCheckBox -> isChecked() ? true : false); qDebug() << "nodes " << nodes ; qDebug() << "bprob " << bprob; qDebug() << "degree" << degree; qDebug() << "mode " << mode; qDebug() << "diag " << diag; emit userChoices(nodes, degree, bprob, mode, diag); } socnetv-2.2/src/dialograndsmallworld.h000644 000765 000024 00000004445 13040734202 021174 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialograndsmallworld.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef DIALOGRANDSMALLWORLD_H #define DIALOGRANDSMALLWORLD_H #include #include "ui_dialograndsmallworld.h" class DialogRandSmallWorld : public QDialog { Q_OBJECT public: explicit DialogRandSmallWorld(QWidget *parent = 0); public slots: void checkErrors(); void gatherData(); void setModeDirected(); void setModeUndirected(); void setDiag(); void modifyDegree(int value); signals: void userChoices( const int nodes, const int degree, const float prob, const QString mode, const bool diag); private: QString mode; int nodes, degree; float bprob; bool diag; Ui::DialogRandSmallWorld *ui; }; #endif socnetv-2.2/src/dialogsettings.cpp000644 000765 000024 00000052005 13040734201 020334 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialogsettings.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include "dialogsettings.h" #include "ui_dialogsettings.h" #include #include #include #include #include #include DialogSettings::DialogSettings( QMap &appSettings, QWidget *parent) : QDialog(parent), m_appSettings(appSettings), ui(new Ui::DialogSettings) { ui->setupUi(this); // m_appSettings = appSettings; //only use if var passed by pointer //data export ui->dataDirEdit->setText( (m_appSettings)["dataDir"]); //debugging ui->printDebugChkBox->setChecked( (appSettings["printDebug"] == "true") ? true:false ); ui->progressBarsChkBox->setChecked( (appSettings["showProgressBar"] == "true") ? true:false ); /** * canvas options */ ui->antialiasingChkBox->setChecked( (appSettings["antialiasing"] == "true") ? true:false ); ui->printLogoChkBox->setChecked( (appSettings["printLogo"] == "true") ? true:false ); m_bgColor = QColor (m_appSettings["initBackgroundColor"]); m_pixmap = QPixmap(60,20) ; m_pixmap.fill( m_bgColor ); ui->bgColorButton->setIcon(QIcon(m_pixmap)); ui->bgImageSelectEdit->setText((m_appSettings)["initBackgroundImage"]); /** * window options */ ui->leftPanelChkBox->setChecked( ( appSettings["showLeftPanel"] == "true") ? true:false ); ui->rightPanelChkBox->setChecked( ( appSettings["showRightPanel"] == "true") ? true:false ); /** * node options */ m_nodeColor = QColor (m_appSettings["initNodeColor"]); m_pixmap = QPixmap(60,20) ; m_pixmap.fill( m_nodeColor ); ui->nodeColorBtn->setIcon(QIcon(m_pixmap)); if (m_appSettings["initNodeShape"] == "box") { ui->nodeShapeRadioBox->setChecked(true); } else if (m_appSettings["initNodeShape"] == "circle") { ui->nodeShapeRadioCircle->setChecked(true); } else if (m_appSettings["initNodeShape"] == "diamond") { ui->nodeShapeRadioDiamond->setChecked(true); } else if (m_appSettings["initNodeShape"] == "ellipse") { ui->nodeShapeRadioEllipse->setChecked(true); } else if (m_appSettings["initNodeShape"] == "triangle") { ui->nodeShapeRadioTriangle->setChecked(true); } else if (m_appSettings["initNodeShape"] == "star") { ui->nodeShapeRadioStar->setChecked(true); } else { // default ui->nodeShapeRadioCircle->setChecked(true); } ui->nodeSizeSpin->setValue( m_appSettings["initNodeSize"].toInt(0, 10) ); ui->nodeNumbersChkBox->setChecked( ( m_appSettings["initNodeNumbersVisibility"] == "true") ? true : false ); ui->nodeNumbersInsideChkBox->setChecked( (m_appSettings["initNodeNumbersInside"] == "true" ) ? true:false ); if (m_appSettings["initNodeNumbersInside"] == "true") { ui->nodeNumberDistanceSpin->setEnabled(false); ui->nodeNumberSizeSpin->setValue( 0 ); } m_nodeNumberColor = QColor (m_appSettings["initNodeNumberColor"]); m_pixmap = QPixmap(60,20) ; m_pixmap.fill( m_nodeNumberColor ); ui->nodeNumberColorBtn->setIcon(QIcon(m_pixmap)); ui->nodeNumberSizeSpin->setValue( m_appSettings["initNodeNumberSize"].toInt(0, 10) ); ui->nodeNumberDistanceSpin->setValue( m_appSettings["initNodeNumberDistance"].toInt(0, 10) ); ui->nodeLabelsChkBox->setChecked( ( m_appSettings["initNodeLabelsVisibility"] == "true") ? true : false ); ui->nodeLabelSizeSpin->setValue( m_appSettings["initNodeLabelSize"].toInt(0, 10) ); m_nodeLabelColor = QColor (m_appSettings["initNodeLabelColor"]); m_pixmap = QPixmap(60,20) ; m_pixmap.fill( m_nodeLabelColor ); ui->nodeLabelColorBtn->setIcon(QIcon(m_pixmap)); ui->nodeLabelDistanceSpin->setValue( m_appSettings["initNodeLabelDistance"].toInt(0, 10) ); /** * edge options */ ui->edgesChkBox-> setChecked( (m_appSettings["initEdgesVisibility"] == "true") ? true: false ); ui->edgeArrowsChkBox-> setChecked( (m_appSettings["initEdgeArrows"] == "true") ? true: false ); m_edgeColor = QColor (m_appSettings["initEdgeColor"]); m_pixmap = QPixmap(60,20) ; m_pixmap.fill( m_edgeColor ); ui->edgeColorBtn->setIcon(QIcon(m_pixmap)); m_edgeColorNegative = QColor (m_appSettings["initEdgeColorNegative"]); m_pixmap = QPixmap(60,20) ; m_pixmap.fill( m_edgeColorNegative ); ui->edgeColorNegativeBtn ->setIcon(QIcon(m_pixmap)); if (m_appSettings["initEdgeShape"] == "line") { ui->edgeShapeRadioStraightLine->setChecked(true); } else if (m_appSettings["initEdgeShape"] == "bezier") { ui->edgeShapeRadioBezier->setChecked(true); } else { ui->edgeShapeRadioStraightLine->setChecked(true); } ui->edgeWeightNumbersChkBox-> setChecked( (m_appSettings["initEdgeWeightNumbersVisibility"] == "true") ? true: false ); m_edgeWeightNumberColor = QColor (m_appSettings["initEdgeWeightNumberColor"]); m_pixmap = QPixmap(60,20) ; m_pixmap.fill( m_edgeWeightNumberColor ); ui->edgeWeightNumberColorBtn->setIcon(QIcon(m_pixmap)); ui->edgeWeightNumberSizeSpin->setValue( m_appSettings["initEdgeWeightNumberSize"].toInt(0, 10) ); ui->edgeLabelsChkBox-> setChecked( (m_appSettings["initEdgeLabelsVisibility"] == "true") ? true: false ); /** * dialog signals to slots */ connect (ui->dataDirSelectButton, &QToolButton::clicked, this, &DialogSettings::getDataDir); connect (ui->printDebugChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setDebugMsgs); connect (ui->antialiasingChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setAntialiasing); connect (ui->printLogoChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setPrintLogo); connect (ui->bgColorButton, &QToolButton::clicked, this, &DialogSettings::getBgColor); connect (ui->bgImageSelectButton, &QToolButton::clicked, this, &DialogSettings::getBgImage); connect (ui->progressBarsChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setProgressBars); connect (ui->showToolBarChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setToolBar); connect (ui->showStatusBarChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setStatusBar); connect (ui->leftPanelChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setLeftPanel); connect (ui->rightPanelChkBox, &QCheckBox::stateChanged, this, &DialogSettings::setRightPanel); connect (ui->nodeShapeRadioBox, &QRadioButton::clicked, this, &DialogSettings::getNodeShape); connect (ui->nodeShapeRadioCircle, &QRadioButton::clicked, this, &DialogSettings::getNodeShape); connect (ui->nodeShapeRadioDiamond, &QRadioButton::clicked, this, &DialogSettings::getNodeShape); connect (ui->nodeShapeRadioEllipse, &QRadioButton::clicked, this, &DialogSettings::getNodeShape); connect (ui->nodeShapeRadioTriangle, &QRadioButton::clicked, this, &DialogSettings::getNodeShape); connect (ui->nodeShapeRadioStar, &QRadioButton::clicked, this, &DialogSettings::getNodeShape); connect(ui->nodeSizeSpin, SIGNAL(valueChanged(int)), this, SLOT(getNodeSize(int)) ); connect ( ui->buttonBox, &QDialogButtonBox::accepted, this, &DialogSettings::validateSettings ); connect (ui->nodeColorBtn, &QToolButton::clicked, this, &DialogSettings::getNodeColor); connect (ui->nodeNumbersChkBox, &QCheckBox::stateChanged, this, &DialogSettings::getNodeNumbersVisibility); connect (ui->nodeNumbersInsideChkBox, &QCheckBox::stateChanged, this, &DialogSettings::getNodeNumbersInside); connect (ui->nodeNumberColorBtn, &QToolButton::clicked, this, &DialogSettings::getNodeNumberColor); connect(ui->nodeNumberSizeSpin, SIGNAL(valueChanged(int)), this, SLOT(getNodeNumberSize(int)) ); connect(ui->nodeNumberDistanceSpin, SIGNAL(valueChanged(int)), this, SLOT(getNodeNumberDistance(int)) ); connect (ui->nodeLabelsChkBox, &QCheckBox::stateChanged, this, &DialogSettings::getNodeLabelsVisibility); connect(ui->nodeLabelSizeSpin, SIGNAL(valueChanged(int)), this, SLOT(getNodeLabelSize(int)) ); connect (ui->nodeLabelColorBtn, &QToolButton::clicked, this, &DialogSettings::getNodeLabelColor); connect(ui->nodeLabelDistanceSpin, SIGNAL(valueChanged(int)), this, SLOT(getNodeLabelDistance(int)) ); connect (ui->edgesChkBox, &QCheckBox::stateChanged, this, &DialogSettings::getEdgesVisibility); connect (ui->edgeArrowsChkBox, &QCheckBox::stateChanged, this, &DialogSettings::getEdgeArrowsVisibility); connect (ui->edgeColorBtn, &QToolButton::clicked, this, &DialogSettings::getEdgeColor); connect (ui->edgeColorNegativeBtn, &QToolButton::clicked, this, &DialogSettings::getEdgeColorNegative); connect (ui->edgeShapeRadioStraightLine, &QRadioButton::clicked, this, &DialogSettings::getEdgeShape); connect (ui->edgeShapeRadioBezier, &QRadioButton::clicked, this, &DialogSettings::getEdgeShape); connect (ui->edgeWeightNumbersChkBox, &QCheckBox::stateChanged, this, &DialogSettings::getEdgeWeightNumbersVisibility); connect (ui->edgeLabelsChkBox, &QCheckBox::stateChanged, this, &DialogSettings::getEdgeLabelsVisibility); } /** * @brief DialogSettings::validateSettings * Validates form data and signals saveSettings to MW */ void DialogSettings::validateSettings(){ emit saveSettings(); } void DialogSettings::getDataDir(){ QString m_dataDir = QFileDialog::getExistingDirectory(this, tr("Select a new data dir"), ui->dataDirEdit->text(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); if (!m_dataDir.isEmpty()) { if (!m_dataDir.endsWith( QDir::separator() )) { m_dataDir += QDir::separator(); } ui->dataDirEdit->setText(m_dataDir); m_appSettings["dataDir"]= m_dataDir; } } /** * @brief DialogSettings::getBgColor * Opens a QColorDialog for the user to select a new bg color */ void DialogSettings::getBgColor(){ m_bgColor = QColorDialog::getColor( m_bgColor, this, tr("Select a background color") ); if ( m_bgColor.isValid()) { m_pixmap.fill(m_bgColor); ui->bgColorButton->setIcon(QIcon(m_pixmap)); ui->bgImageSelectEdit->setText(""); m_appSettings["initBackgroundColor"] = m_bgColor.name(); m_appSettings["initBackgroundImage"] = ""; emit setBgColor(m_bgColor); } else { // user pressed Cancel } } /** * @brief DialogSettings::getBgImage */ void DialogSettings::getBgImage(){ QString m_bgImage = QFileDialog::getOpenFileName( this, tr("Select a background image "), (m_appSettings)["lastUsedDirPath"], tr("All (*);;PNG (*.png);;JPG (*.jpg)") ); if (!m_bgImage.isEmpty() ) { (m_appSettings)["initBackgroundImage"] = m_bgImage ; ui->bgImageSelectEdit->setText((m_appSettings)["initBackgroundImage"]); emit setBgImage(); } else { //user pressed Cancel } } /** * @brief DialogSettings::getNodeColor * * Opens a QColorDialog for the user to select a new node color */ void DialogSettings::getNodeColor(){ m_nodeColor = QColorDialog::getColor( m_nodeColor, this, tr("Select a color for Nodes") ); if ( m_nodeColor.isValid()) { m_pixmap.fill(m_nodeColor); ui->nodeColorBtn->setIcon(QIcon(m_pixmap)); (m_appSettings)["initNodeColor"] = m_nodeColor.name(); emit setNodeColor(m_nodeColor); } else { // user pressed Cancel } } /** * @brief DialogSettings::getNodeShape */ void DialogSettings::getNodeShape(){ QString nodeShape; if ( ui->nodeShapeRadioBox->isChecked () ){ m_appSettings["initNodeShape"] = "box"; } else if ( ui->nodeShapeRadioCircle->isChecked() ){ m_appSettings["initNodeShape"] = "circle"; } else if ( ui->nodeShapeRadioDiamond->isChecked() ){ m_appSettings["initNodeShape"] = "diamond"; } else if ( ui->nodeShapeRadioEllipse->isChecked() ){ m_appSettings["initNodeShape"] = "ellipse"; } else if ( ui->nodeShapeRadioTriangle->isChecked() ){ m_appSettings["initNodeShape"] = "triangle"; } else if ( ui->nodeShapeRadioStar->isChecked() ){ m_appSettings["initNodeShape"] = "star"; } else { m_appSettings["initNodeShape"] = "box"; } qDebug()<< "DialogSettings::getNodeShape() - new default shape " << nodeShape; emit setNodeShape(m_appSettings["initNodeShape"], 0); } /** * @brief DialogSettings::getNodeSize * @param size */ void DialogSettings::getNodeSize( int size) { m_appSettings["initNodeSize"]= QString::number(size); emit setNodeSize(size, false); } /** * @brief DialogSettings::getNodeNumbersVisibility * @param toggle */ void DialogSettings::getNodeNumbersVisibility (bool toggle){ m_appSettings["initNodeNumbersVisibility"]= (toggle) ? "true" : "false"; emit setNodeNumbersVisibility(toggle); } /** * @brief DialogSettings::getNodeNumbersInside * @param toggle */ void DialogSettings::getNodeNumbersInside(bool toggle) { m_appSettings["initNodeNumbersInside"]= (toggle) ? "true" : "false"; ui->nodeNumbersChkBox->setChecked(true); ui->nodeNumberDistanceSpin->setEnabled(!toggle); ui->nodeNumberSizeSpin->setValue( ( (toggle) ? 0 : 7) ); emit setNodeNumbersInside(toggle); } /** * @brief DialogSettings::getNodeNumberSize * @param size */ void DialogSettings::getNodeNumberSize( const int size) { m_appSettings["initNodeNumberSize"]= QString::number(size); emit setNodeNumberSize(0, size, false); } /** * @brief DialogSettings::getNodeNumberDistance * @param distance */ void DialogSettings::getNodeNumberDistance(const int distance) { m_appSettings["initNodeNumberDistance"]= QString::number(distance); emit setNodeNumberDistance(0, distance); } /** * @brief DialogSettings::getNodeNumberColor * * Opens a QColorDialog for the user to select a new node number color */ void DialogSettings::getNodeNumberColor(){ m_nodeNumberColor = QColorDialog::getColor( m_nodeNumberColor, this, tr("Select color for Node Numbers") ); if ( m_nodeNumberColor.isValid()) { m_pixmap.fill(m_nodeNumberColor); ui->nodeNumberColorBtn->setIcon(QIcon(m_pixmap)); (m_appSettings)["initNodeNumberColor"] = m_nodeNumberColor.name(); emit setNodeNumberColor(m_nodeNumberColor); } else { // user pressed Cancel } } /** * @brief DialogSettings::getNodeLabelsVisibility * @param toggle */ void DialogSettings::getNodeLabelsVisibility (bool toggle){ m_appSettings["initNodeLabelsVisibility"]= (toggle) ? "true" : "false"; emit setNodeLabelsVisibility(toggle); } /** * @brief DialogSettings::getNodeLabelColor * * Opens a QColorDialog for the user to select a new node Label color */ void DialogSettings::getNodeLabelColor(){ m_nodeLabelColor = QColorDialog::getColor( m_nodeLabelColor, this, tr("Select color for Node Labels") ); if ( m_nodeLabelColor.isValid()) { m_pixmap.fill(m_nodeLabelColor); ui->nodeLabelColorBtn->setIcon(QIcon(m_pixmap)); (m_appSettings)["initNodeLabelColor"] = m_nodeLabelColor.name(); emit setNodeLabelColor(m_nodeLabelColor); } else { // user pressed Cancel } } /** * @brief DialogSettings::getNodeLabelSize * @param size */ void DialogSettings::getNodeLabelSize( const int size) { m_appSettings["initNodeLabelSize"]= QString::number(size); emit setNodeLabelSize(0, size); } /** * @brief DialogSettings::getNodeLabelDistance * @param distance */ void DialogSettings::getNodeLabelDistance(const int distance) { m_appSettings["initNodeLabelDistance"]= QString::number(distance); emit setNodeLabelDistance(0, distance); } /** * @brief DialogSettings::getEdgesVisibility * @param toggle */ void DialogSettings::getEdgesVisibility (const bool &toggle){ m_appSettings["initEdgesVisibility"]= (toggle) ? "true" : "false"; emit setEdgesVisibility(toggle); } /** * @brief DialogSettings::getEdgeArrowsVisibility * @param toggle */ void DialogSettings::getEdgeArrowsVisibility(const bool &toggle){ m_appSettings["initEdgeArrows"]= (toggle) ? "true" : "false"; emit setEdgeArrowsVisibility(toggle); } /** * @brief DialogSettings::getEdgeColor * * Opens a QColorDialog for the user to select a new edge color */ void DialogSettings::getEdgeColor(){ m_edgeColor = QColorDialog::getColor( m_edgeColor, this, tr("Select color for Edges ") ); if ( m_edgeColor.isValid()) { m_pixmap.fill(m_edgeColor); ui->edgeColorBtn->setIcon(QIcon(m_pixmap)); (m_appSettings)["initEdgeColor"] = m_edgeColor.name(); emit setEdgeColor(m_edgeColor, RAND_MAX); } else { // user pressed Cancel } } /** * @brief DialogSettings::getEdgeColorNegative * * Opens a QColorDialog for the user to select a new edge color */ void DialogSettings::getEdgeColorNegative(){ m_edgeColorNegative = QColorDialog::getColor( m_edgeColorNegative, this, tr("Select color for negative Edges") ); if ( m_edgeColorNegative.isValid()) { m_pixmap.fill(m_edgeColorNegative); ui->edgeColorNegativeBtn->setIcon(QIcon(m_pixmap)); (m_appSettings)["initEdgeColorNegative"] = m_edgeColorNegative.name(); emit setEdgeColor(m_edgeColorNegative, 0); } else { // user pressed Cancel } } /** * @brief DialogSettings::getEdgeShape */ void DialogSettings::getEdgeShape(){ if ( ui->edgeShapeRadioStraightLine->isChecked () ){ m_appSettings["initEdgeShape"] = "line"; } else if ( ui->edgeShapeRadioBezier->isChecked() ){ m_appSettings["initEdgeShape"] = "bezier"; } qDebug()<< "DialogSettings::getEdgeShape() - new default shape " << m_appSettings["initEdgeShape"]; emit setEdgeShape(m_appSettings["initEdgeShape"], 0); } /** * @brief DialogSettings::getEdgeWeightNumbersVisibility * @param toggle */ void DialogSettings::getEdgeWeightNumbersVisibility(const bool &toggle){ m_appSettings["initEdgeWeightNumbersVisibility"]= (toggle) ? "true" : "false"; emit setEdgeWeightNumbersVisibility(toggle); } /** * @brief DialogSettings::getEdgeLabelsVisibility * @param toggle */ void DialogSettings::getEdgeLabelsVisibility(const bool &toggle){ m_appSettings["initEdgeLabelsVisibility"]= (toggle) ? "true" : "false"; emit setEdgeLabelsVisibility(toggle); } DialogSettings::~DialogSettings() { delete ui; } socnetv-2.2/src/dialogsettings.h000644 000765 000024 00000010237 13040734202 020003 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialogsettings.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef DIALOGSETTINGS_H #define DIALOGSETTINGS_H #include #include namespace Ui { class DialogSettings; } class DialogSettings : public QDialog { Q_OBJECT public: explicit DialogSettings(QMap &appSettings, QWidget *parent = 0 ); ~DialogSettings(); public slots: void getDataDir(); void getBgColor(); void getBgImage(); void validateSettings(); void getNodeColor(); void getNodeShape(); void getNodeSize(int); void getNodeNumbersVisibility (bool toggle); void getNodeNumbersInside(bool toggle); void getNodeNumberColor(); void getNodeNumberSize(const int); void getNodeNumberDistance(const int); void getNodeLabelsVisibility (bool toggle); void getNodeLabelColor(); void getNodeLabelSize(const int); void getNodeLabelDistance(const int); void getEdgesVisibility (const bool &toggle); void getEdgeArrowsVisibility (const bool &toggle); void getEdgeColor(); void getEdgeColorNegative(); void getEdgeShape(); void getEdgeWeightNumbersVisibility(const bool &toggle); void getEdgeLabelsVisibility(const bool &toggle); signals: void setProgressBars(bool); void setToolBar(bool); void setStatusBar(bool); void setAntialiasing(bool); void setPrintLogo(bool); void setDebugMsgs(bool); void setBgColor(const QColor); void setBgImage(); void setRightPanel(bool); void setLeftPanel(bool); void setNodeColor(QColor); void setNodeShape(const QString, const long int); void setNodeSize(int, const bool &); void setNodeNumbersVisibility(bool); void setNodeNumbersInside(bool); void setNodeNumberSize(const int v, const int &size, const bool prompt); void setNodeNumberDistance(const int v, const int &); void setNodeNumberColor(const QColor); void setNodeLabelsVisibility(const bool &); void setNodeLabelColor(const QColor); void setNodeLabelSize(const int v, const int &); void setNodeLabelDistance(const int v, const int &); void setEdgesVisibility (const bool &toggle); void setEdgeArrowsVisibility (const bool &toggle); void setEdgeColor(const QColor, const int &); void setEdgeShape(const QString, const long int); void setEdgeWeightNumbersVisibility(const bool &toggle); void setEdgeLabelsVisibility(const bool &toggle); void saveSettings(); private: QMap &m_appSettings ; Ui::DialogSettings *ui; QPixmap m_pixmap; //QString m_nodeShape; QColor m_bgColor, m_nodeColor, m_nodeNumberColor, m_nodeLabelColor; QColor m_edgeColor, m_edgeColorNegative, m_edgeWeightNumberColor; }; #endif socnetv-2.2/src/dialogsimilaritymatches.cpp000644 000765 000024 00000006353 13040734201 022234 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialogsimilaritymatches.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include "dialogsimilaritymatches.h" #include #include DialogSimilarityMatches::DialogSimilarityMatches (QWidget *parent) : QDialog (parent) { ui.setupUi(this); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); matrixList << "Adjacency" << "Distances"; variablesLocationList << "Rows" << "Columns" << "Both"; measureList << "Simple / Exact matching" <<"Jaccard index" <<"Hamming distance" <<"Cosine similarity" <<"Euclidean distance"; ui.matrixSelect -> insertItems( 1, matrixList ); (ui.variablesLocationSelect) -> insertItems( 1, variablesLocationList ); (ui.measureSelect) -> insertItems( 1, measureList ); (ui.diagonalCheckBox)->setChecked(false); } void DialogSimilarityMatches::gatherData(){ qDebug()<< "DialogSimilarityMatches: gathering Data!..."; QString matrix = (ui.matrixSelect) ->currentText(); QString varLocation = (ui.variablesLocationSelect) ->currentText(); QString measure = (ui.measureSelect)->currentText(); bool diagonal = (ui.diagonalCheckBox)->isChecked(); qDebug()<< "DialogSimilarityMatches: user selected: " << matrix << varLocation << measure; emit userChoices( matrix, varLocation, measure, diagonal ); } void DialogSimilarityMatches::on_buttonBox_accepted() { this->gatherData(); this->accept(); } void DialogSimilarityMatches::on_buttonBox_rejected() { this->reject(); } DialogSimilarityMatches::~DialogSimilarityMatches(){ matrixList.clear(); variablesLocationList.clear(); } socnetv-2.2/src/dialogsimilaritymatches.h000644 000765 000024 00000004366 13040734202 021704 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialogsimilaritymatches.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef DIALOGSIMILARITYMATCHES_H #define DIALOGSIMILARITYMATCHES_H #include #include "ui_dialogsimilaritymatches.h" class DialogSimilarityMatches: public QDialog { Q_OBJECT public: DialogSimilarityMatches (QWidget *parent = 0); ~DialogSimilarityMatches(); public slots: void gatherData(); signals: void userChoices(const QString &matrix, const QString &varLocation, const QString &method, const bool &diagonal); private slots: void on_buttonBox_accepted(); void on_buttonBox_rejected(); private: Ui::DialogSimilarityMatches ui; QStringList matrixList, variablesLocationList, measureList; }; #endif socnetv-2.2/src/dialogsimilaritypearson.cpp000644 000765 000024 00000005617 13040734201 022261 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialogsimilaritypearson.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include "dialogsimilaritypearson.h" #include #include DialogSimilarityPearson::DialogSimilarityPearson (QWidget *parent) : QDialog (parent) { ui.setupUi(this); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); matrixList << "Adjacency" << "Distances"; variablesLocationList << "Rows" << "Columns" << "Both"; (ui.matrixSelect) -> insertItems( 1, matrixList ); (ui.variablesLocationSelect) -> insertItems( 1, variablesLocationList ); (ui.diagonalCheckBox)->setChecked(false); } void DialogSimilarityPearson::gatherData(){ qDebug()<< "DialogSimilarityPearson: gathering Data!..."; QString matrix = (ui.matrixSelect) ->currentText(); QString varLocation = (ui.variablesLocationSelect) ->currentText(); bool diagonal = (ui.diagonalCheckBox)->isChecked(); qDebug()<< "DialogSimilarityPearson: user selected: " << matrix << varLocation; emit userChoices( matrix, varLocation,diagonal ); } void DialogSimilarityPearson::on_buttonBox_accepted() { this->gatherData(); this->accept(); } void DialogSimilarityPearson::on_buttonBox_rejected() { this->reject(); } DialogSimilarityPearson::~DialogSimilarityPearson(){ matrixList.clear(); variablesLocationList.clear(); } socnetv-2.2/src/dialogsimilaritypearson.h000644 000765 000024 00000004275 13040734202 021726 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt dialogsimilaritypearson.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef DIALOGSIMILARITYPEARSON_H #define DIALOGSIMILARITYPEARSON_H #include #include "ui_dialogsimilaritypearson.h" class DialogSimilarityPearson: public QDialog { Q_OBJECT public: DialogSimilarityPearson (QWidget *parent = 0); ~DialogSimilarityPearson(); public slots: void gatherData(); signals: void userChoices(const QString &matrix, const QString &varLocation, const bool &diagonal); private slots: void on_buttonBox_accepted(); void on_buttonBox_rejected(); private: Ui::DialogSimilarityPearson ui; QStringList matrixList, variablesLocationList; }; #endif socnetv-2.2/src/edge.cpp000755 000765 000024 00000041565 13040734201 016234 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt edge.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include #include #include #include #include //used for qDebug messages #include #include "graphicswidget.h" #include "edge.h" #include "node.h" #include "edgeweight.h" #include "edgelabel.h" static const double Pi = 3.14159265; static double TwoPi = 2.0 * Pi; static const int EDGE_DIRECTED = 0; static const int EDGE_DIRECTED_OPPOSITE_EXISTS = 1; static const int EDGE_RECIPROCAL_UNDIRECTED = 2; Edge::Edge( GraphicsWidget *gw, Node *from, Node *to, const float &weight, const QString &label, const QString &color, const Qt::PenStyle &style, const int &type, const bool &drawArrows, const bool &bezier, const bool &weightNumbers) : graphicsWidget(gw) { graphicsWidget->scene()->addItem(this); //add edge to scene to be displayed from->addOutLink( this ); //adds this Edge to sourceNode to->addInLink( this ); //adds this Edge to targetNode source=from; //saves the sourceNode target=to; //Saves the targetNode m_style = style; m_state = EDGE_STATE_REGULAR ; m_color=color; m_drawArrows=drawArrows; m_edgeType=type; m_directed_first = false; m_startOffset=source->size(); //used to offset edge from the centre of node m_endOffset=target->size(); //used to offset edge from the centre of node m_arrowSize=4; //controls the width of the edge arrow eFrom = source->nodeNumber() ; eTo = target->nodeNumber() ; m_weight = weight ; m_Bezier = bezier; m_label = label; m_drawLabel = !m_label.isEmpty(); m_drawWeightNumber = weightNumbers; qDebug()<< "Edge::Edge(): " << eFrom << "->" << eTo <<" = " << m_weight <<" label " << m_label <<" edgeType " << m_edgeType; if (m_drawWeightNumber) { addWeightNumber(); } if (m_drawLabel) addLabel(); setAcceptHoverEvents(true); setFlags(QGraphicsItem::ItemIsSelectable); //Edges have lower z than nodes. Nodes always appear above edges. setZValue(ZValueEdge); setBoundingRegionGranularity(0); //setCacheMode (QGraphicsItem::ItemCoordinateCache); m_path = new QPainterPath; m_path_shape = new QPainterPath; adjust(); } void Edge::showArrows(const bool &drawArrows){ prepareGeometryChange(); m_drawArrows=drawArrows; adjust(); } void Edge::removeRefs(){ qDebug("Edge: removeRefs()"); source->deleteOutLink(this); target->deleteInLink(this); } void Edge::setColor( const QString &str) { m_color=str; prepareGeometryChange(); } QString Edge::color() const{ return m_color; } /** * @brief Called from Graph::graphSaveToPajekFormat() * @return */ QString Edge::colorToPajek() { if (m_color.startsWith("#")) { return ("RGB"+m_color.right( m_color.size()-1 )).toUpper() ; } return m_color; } /** * @brief Called from MW when user wants to change an edge's weight. Updates both the width and the weightNumber * @param w */ void Edge::setWeight(const float &w) { qDebug() << "Edge::setWeight() " << w; prepareGeometryChange(); m_weight = w; if (m_drawWeightNumber) weightNumber->setPlainText (QString::number(w)); } float Edge::weight() const { qDebug() << "Edge::weight() " << m_weight; return m_weight; } void Edge::addWeightNumber (){ // create edge weight item double x = -20 + ( source->x() + target->x() ) / 2.0; double y = -20 + ( source->y() + target->y() ) / 2.0; weightNumber = new EdgeWeight (this, 7, QString::number(m_weight) ); weightNumber-> setPos(x,y); weightNumber-> setDefaultTextColor (m_color); m_drawWeightNumber = true; } void Edge::setWeightNumberVisibility (const bool &toggle) { if (m_drawWeightNumber) { if (toggle) weightNumber ->show(); else weightNumber ->hide(); } else{ if (toggle) addWeightNumber(); } } /** * @brief Called from MW when user wants to change an edge's label * @param label */ void Edge::setLabel(const QString &label) { qDebug() << "Edge::setLabel() " << label; prepareGeometryChange(); m_label = label; if (m_drawLabel) edgeLabel->setPlainText (m_label); } QString Edge::label() const { return m_label; } void Edge::addLabel (){ // create edge label item double x = 5+ ( source->x() + target->x() ) / 2.0; double y = 5+ ( source->y() + target->y() ) / 2.0; edgeLabel = new EdgeLabel (this, 7, m_label ); edgeLabel-> setPos(x,y); edgeLabel-> setDefaultTextColor (m_color); m_drawLabel = true; } void Edge::setLabelVisibility (const bool &toggle) { if (m_drawLabel) { if (toggle) edgeLabel ->show(); else edgeLabel ->hide(); } else{ if (toggle) addLabel(); } } void Edge::setStartOffset(const int &offset){ m_startOffset=offset; } void Edge::setEndOffset(int offset){ m_endOffset=offset; } Node *Edge::sourceNode() const { return source; } void Edge::setSourceNode(Node *node) { source = node; adjust(); } Node *Edge::targetNode() const { return target; } void Edge::setTargetNode(Node *node){ target = node; adjust(); } int Edge::sourceNodeNumber () { return eFrom; } int Edge::targetNodeNumber() { return eTo; } /** * @brief Leaves some empty space (offset) from node - * make the edge weight appear on the centre of the edge */ void Edge::adjust(){ // qDebug() << "Edge::adjust()"; if (!source || !target) return; //QLineF line(mapFromItem(source, 0, 0), mapFromItem(target, 0, 0)); QLineF line(source->x(), source->y(), target->x(), target->y()); QPointF edgeOffset; line_length = line.length(); line_dx = line.dx(); line_dy = line.dy(); if (source!=target) { edgeOffset = QPointF( (line_dx * m_endOffset) / line_length, (line_dy * m_endOffset) / line_length); } else edgeOffset = QPointF(0, 0); prepareGeometryChange(); sourcePoint = line.p1() + edgeOffset ; targetPoint = line.p2() - edgeOffset ; if (m_edgeType == EDGE_DIRECTED_OPPOSITE_EXISTS ) { if (m_directed_first ) { sourcePoint -= QPointF(4,4); targetPoint -= QPointF(4,4); } else { sourcePoint += QPointF(4,4); targetPoint += QPointF(4,4); } } if (m_drawWeightNumber) weightNumber->setPos( -20 + (source->x()+target->x())/2.0, -20+ (source->y()+target->y())/2.0 ); if (m_drawLabel) edgeLabel->setPos( 5+ (source->x()+target->x())/2.0, 5+ (source->y()+target->y())/2.0 ); //Define the path upon which we' ll draw the line //QPainterPath line(sourcePoint); m_path = new QPainterPath(sourcePoint); //Construct the path if (source!=target) { if ( !m_Bezier){ // qDebug()<< "*** Edge::paint(). Constructing a line"; m_path->lineTo(targetPoint); } else { qDebug() << "*** Edge::paint(). Constructing a bezier curve"; } } else { //self-link QPointF c1 = QPointF( targetPoint.x() -30, targetPoint.y() -30 ); QPointF c2 = QPointF( targetPoint.x() +30, targetPoint.y() -30 ); // qDebug()<< "*** Edge::paint(). Constructing a bezier self curve c1 " // <cubicTo( c1, c2, targetPoint); } //Draw the arrows only if we have different nodes //and the nodes are enough far apart from each other if (m_drawArrows && source!=target && line_length > 10) { angle = 0; // line_length = m_path->length(); // line_dx = targetPoint.x()-sourcePoint.x(); // line_dy = targetPoint.y()-sourcePoint.y(); if ( line_length > 0 ) angle = ::acos( line_dx / line_length ); // qDebug() << " acos() " << ::acos( line_dx / line_length ) ; if ( line_dy >= 0) angle = TwoPi - angle; // qDebug() << "*** Edge::paint(). Constructing arrows. " // "First Arrow at target node" // << "target-source: " << line_dx // << " length: " << line_length // << " angle: "<< angle; QPointF destArrowP1 = targetPoint + QPointF(sin(angle - Pi / 3) * m_arrowSize, cos(angle - Pi / 3) * m_arrowSize); QPointF destArrowP2 = targetPoint + QPointF(sin(angle - Pi + Pi / 3) * m_arrowSize, cos(angle - Pi + Pi / 3) * m_arrowSize); // qDebug() << "*** Edge::paint() destArrowP1 " // << destArrowP1.x() << "," << destArrowP1.y() // << " destArrowP2 " << destArrowP2.x() << "," << destArrowP2.y(); m_path->addPolygon ( QPolygonF() << targetPoint << destArrowP1 << destArrowP2 << targetPoint ); if (m_edgeType == EDGE_RECIPROCAL_UNDIRECTED ) { // qDebug() << "**** Edge::paint() This edge is SYMMETRIC! " // << " So, we need to create Arrow at src node as well"; QPointF srcArrowP1 = sourcePoint + QPointF(sin(angle +Pi / 3) * m_arrowSize, cos(angle +Pi / 3) * m_arrowSize); QPointF srcArrowP2 = sourcePoint + QPointF(sin(angle +Pi - Pi / 3) * m_arrowSize, cos(angle +Pi - Pi / 3) * m_arrowSize); m_path->addPolygon ( QPolygonF() << sourcePoint << srcArrowP1 << srcArrowP2 <addPath(m_path->translated(1,1)); m_path_shape->addPath(m_path->translated(-1,-1)); return *m_path_shape; } /** * @brief Defines the outer bounds of the edge as a rectangle; * All painting will be restricted to inside the edge's bounding rect. * Qt uses this bounding rect to determine whether the edge requires redrawing. * @return */ QRectF Edge::boundingRect() const { if (!source || !target) return QRectF(); return m_path->controlPointRect(); } void Edge::setDirectedWithOpposite(){ qDebug()<< "Edge::setDirectedWithOpposite()"; prepareGeometryChange(); m_edgeType = EDGE_DIRECTED_OPPOSITE_EXISTS; m_directed_first= true; } void Edge::setUndirected(){ qDebug()<< "Edge::setUndirected()"; prepareGeometryChange(); m_edgeType = EDGE_RECIPROCAL_UNDIRECTED; m_directed_first= false; m_drawArrows = false; adjust(); } bool Edge::isUndirected() { return ( m_edgeType == EDGE_RECIPROCAL_UNDIRECTED ) ? true:false; } void Edge::setStyle( const Qt::PenStyle &style ) { m_style = style; } Qt::PenStyle Edge::style() const{ return m_style; } /** * @brief Edge::pen * @return */ QPen Edge::pen() const { switch (m_state) { case EDGE_STATE_REGULAR: //qDebug() << "Edge::pen() - returning pen for state REGULAR" ; if (m_weight < 0 ){ return QPen(QColor(m_color), width(), Qt::DashLine, Qt::RoundCap, Qt::RoundJoin); } return QPen(QColor(m_color), width(), style(), Qt::RoundCap, Qt::RoundJoin); break; case EDGE_STATE_HIGHLIGHT: // selected //qDebug() << "Edge::pen() - returning pen for state HIGHLIGHTED" ; return QPen(QColor("red"), width(), style(), Qt::RoundCap, Qt::RoundJoin); case EDGE_STATE_HOVER: // hover //qDebug() << "Edge::pen() - returning pen for state HOVER" ; return QPen(QColor("red"), width()+1, style(), Qt::RoundCap, Qt::RoundJoin); default: //qDebug() << "Edge::pen() - returning pen for state DEFAULT" ; return QPen(QColor(m_color), width(), style(), Qt::RoundCap, Qt::RoundJoin); } } void Edge::setState(const int &state) { //NOTE: DO NOT USE HERE: prepareGeometryChange() m_state=state; } void Edge::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *){ if (!source || !target) return; //qDebug() <<"@@@ Edge::paint()"; //if the edge is being dragged around, darken it! if (option->state & QStyle::State_Selected) { setZValue(ZValueEdgeHighlighted); setState(EDGE_STATE_HOVER); } else if (option->state & QStyle::State_MouseOver) { setZValue(ZValueEdgeHighlighted); setState(EDGE_STATE_HOVER); } else if (m_state==EDGE_STATE_HIGHLIGHT){ setZValue(ZValueEdgeHighlighted); setState(EDGE_STATE_HIGHLIGHT); } else { setZValue(ZValueEdge); setState(EDGE_STATE_REGULAR); } // set painter pen to correct edge pen painter->setPen(pen()); painter->drawPath(*m_path); } /** Controls the width of the edge; is a function of edge weight */ float Edge::width() const{ if ( fabs(m_weight) > 1 ) { // qDebug()<< "Edge::width() -" // << "m_weight" << m_weight // <<"Returning "<< 1 + log(fabs(m_weight)) ; return 1+log(fabs(m_weight)) ; } // qDebug()<< "Edge::width() - Returning"<< m_weight; return m_weight; // Default, if m_weight in (-1, 1) space } /** * @brief Edge::highlight * @param flag */ void Edge::highlight(const bool &flag) { //qDebug()<< "Edge::highlight() - " << flag; if (flag) { prepareGeometryChange(); setState(EDGE_STATE_HIGHLIGHT); } else { prepareGeometryChange(); setState(EDGE_STATE_REGULAR); } } /** handles the events of a click on an edge*/ void Edge::mousePressEvent(QGraphicsSceneMouseEvent *event) { qDebug() <<"Edge::mousePressEvent()"; QGraphicsItem::mousePressEvent(event); } Edge::~Edge(){ qDebug() << "*** ~Edge() - edge " << sourceNodeNumber()<< "->" << targetNodeNumber(); removeRefs(); if (m_drawWeightNumber) graphicsWidget->removeItem(weightNumber); if (m_drawLabel) graphicsWidget->removeItem(edgeLabel); this->hide(); graphicsWidget->removeItem(this); if ( ! m_path->isEmpty()) delete m_path; } socnetv-2.2/src/edge.h000755 000765 000024 00000010714 13040734202 015672 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt edge.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef EDGE_H #define EDGE_H #include #include #include //declares pair construct class GraphicsWidget; class QGraphicsSceneMouseEvent; class Node; class EdgeWeight; class EdgeLabel; using namespace std; static const int TypeEdge= QGraphicsItem::UserType+2; static const int ZValueEdge = 50; static const int ZValueEdgeHighlighted = 99; static const int EDGE_STATE_REGULAR = 0; static const int EDGE_STATE_HIGHLIGHT = 1; static const int EDGE_STATE_HOVER = 2; class Edge : public QObject, public QGraphicsItem { Q_OBJECT Q_INTERFACES (QGraphicsItem) public: Edge(GraphicsWidget *, Node*, Node*, const float &weight, const QString &label, const QString &color, const Qt::PenStyle &style, const int&type, const bool & drawArrows, const bool &bezier, const bool &weightNumbers=false); ~Edge(); enum { Type = UserType + 2 }; int type() const { return Type; } Node *sourceNode() const; void setSourceNode(Node *node); Node *targetNode() const; void setTargetNode(Node *node); void setStartOffset(const int & ); void setEndOffset(int ); void removeRefs(); int sourceNodeNumber(); int targetNodeNumber(); void setWeight( const float &w) ; float weight() const; void addWeightNumber (); //void deleteWeightNumber(); void setWeightNumberVisibility (const bool &toggle); void setLabel( const QString &label) ; QString label() const; void addLabel(); //void deleteLabel(); void setLabelVisibility (const bool &toggle); void showArrows(const bool &); void toggleAntialiasing(bool); void setUndirected(); bool isUndirected(); void setDirectedWithOpposite(); float width() const; QPen pen() const; void setState(const int &state); void setStyle( const Qt::PenStyle &style); Qt::PenStyle style() const; void setColor( const QString &str) ; QString color() const ; QString colorToPajek(); void highlight (const bool &flag); QPainterPath shape() const; public slots: void adjust (); protected: QRectF boundingRect() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); void mousePressEvent(QGraphicsSceneMouseEvent *event); private: GraphicsWidget *graphicsWidget; Node *source, *target; QPainterPath *m_path, *m_path_up, *m_path_down, *m_path_shape; QPointF sourcePoint, targetPoint; qreal m_arrowSize, m_startOffset, m_endOffset; Qt::PenStyle m_style; int m_state; EdgeWeight* weightNumber; EdgeLabel* edgeLabel; QString m_color, m_colorNegative, m_label; int eFrom, eTo; float m_weight; int tox1, tox2, toy1, toy2, size; int m_edgeType; double rad, theta, theta1, theta2; qreal angle, line_length, line_dx, line_dy; bool m_Bezier, m_drawArrows, m_directed_first, m_drawWeightNumber; bool m_drawLabel; }; #endif socnetv-2.2/src/edgelabel.cpp000755 000765 000024 00000004016 13040734201 017222 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt edgelabel.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include "edgelabel.h" #include "edge.h" #include #include EdgeLabel::EdgeLabel( Edge *link , int size, QString labelText) : QGraphicsTextItem( 0) { qDebug()<< "EdgeLabel:: creating new edgelabel and attaching it to link"; setPlainText( labelText ); setParentItem(link); //auto disables child items like this, when link is disabled. this->setFont( QFont ("Courier", size, QFont::Light, true) ); setZValue(ZValueEdgeLabel); } EdgeLabel::~EdgeLabel() { } socnetv-2.2/src/edgelabel.h000755 000765 000024 00000003672 13040734202 016677 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt edgelabel.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef EDGELABEL_H #define EDGELABEL_H #include class Edge; static const int TypeEdgeLabel = QGraphicsItem::UserType+6; static const int ZValueEdgeLabel = 80; class EdgeLabel: public QGraphicsTextItem { public: EdgeLabel(Edge * , int, QString); void removeRefs(); enum { Type = UserType + 6 }; int type() const { return Type; } ~EdgeLabel(); private: }; #endif socnetv-2.2/src/edgeweight.cpp000755 000765 000024 00000004027 13040734201 017434 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt edgeweight.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include "edgeweight.h" #include "edge.h" #include #include EdgeWeight::EdgeWeight( Edge *link , int size, QString labelText) : QGraphicsTextItem( 0) { qDebug()<< "EdgeWeight:: creating new edgeweight and attaching it to link"; setPlainText( labelText ); setParentItem(link); //auto disables child items like this, when link is disabled. this->setFont( QFont ("Courier", size, QFont::Light, true) ); setZValue(ZValueEdgeWeight); } EdgeWeight::~EdgeWeight() { } socnetv-2.2/src/edgeweight.h000755 000765 000024 00000003702 13040734202 017101 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt edgeweight.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef EDGEWEIGHT_H #define EDGEWEIGHT_H #include class Edge; static const int TypeEdgeWeight = QGraphicsItem::UserType+5; static const int ZValueEdgeWeight = 80; class EdgeWeight: public QGraphicsTextItem { public: EdgeWeight(Edge * , int, QString); void removeRefs(); enum { Type = UserType + 5 }; int type() const { return Type; } ~EdgeWeight(); private: }; #endif socnetv-2.2/src/forms/000755 000765 000024 00000000000 13040701535 015740 5ustar00dimitrisstaff000000 000000 socnetv-2.2/src/graph.cpp000755 000765 000024 00002437564 13040734201 016443 0ustar00dimitrisstaff000000 000000 /****************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt graph.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : http://socnetv.org *******************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include "graph.h" #include #include #include #include //used for qDebug messages #include #include #include #include #include //allows the use of RAND_MAX macro #include #include //for BFS queue Q #include // for randomizeThings static qreal Pi = 3.14159265; /** * @brief Graph::Graph * constructor */ Graph::Graph() { m_totalVertices=0; m_totalEdges=0; outboundEdgesVert=0; inboundEdgesVert=0; reciprocalEdgesVert=0; order=true; //returns true if the indexes of the list is ordered. graphModifiedFlag=false; m_graphName=""; m_curRelation=0; m_fileFormat=FILE_UNRECOGNIZED; m_undirected=false; m_isWeighted=false; m_symmetric=true; m_graphDensity = -1; fileName =""; calculatedGraphSymmetry = false; calculatedGraphWeighted = false; calculatedGraphDensity = false; calculatedEdges = false; calculatedVertices=false; calculatedVerticesList = false; calculatedVerticesSet = false; calculatedAdjacencyMatrix=false; calculatedDistances=false; calculatedIsolates = false; calculatedDP=false; calculatedDC=false; calculatedIC=false; calculatedCentralities=false; calculatedIRCC=false; calculatedPP=false; calculatedPRP=false; calculatedTriad=false; m_precision = 3; m_vertexClicked = 0; m_clickedEdge.v1=0; m_clickedEdge.v2=0; file_parser = 0; wc_parser = 0; wc_spider = 0; // edgesHash.reserve(40000); influenceDomains.reserve(1000); influenceRanges.reserve(1000); m_graphFileFormatExportSupported<< FILE_GRAPHML << FILE_PAJEK << FILE_ADJACENCY; randomizeThings(); htmlHead = QString("" "" "" "" "" "" "" "" "" "" "" "" "").arg(VERSION); htmlHeadLight = QString("" "" "" "" "" "" "" "" "" "" "").arg(VERSION); htmlEnd = ""; } /** Clears all vertices */ void Graph::clear() { qDebug()<< "Graph::clear() - m_graph reports size "< 0) { qDebug() << "\n\n\n\n Graph::clear() clearing DM\n\n\n"; DM.clear(); } if ( TM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing DM\n\n\n"; TM.clear(); } if ( sumM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing DM\n\n\n"; sumM.clear(); } if ( invAM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing DM\n\n\n"; invAM.clear(); } if ( AM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing DM\n\n\n"; AM.clear(); } if ( invM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing DM\n\n\n"; invM.clear(); } if ( XM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing DM\n\n\n"; XM.clear(); } if ( XSM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing DM\n\n\n"; XSM.clear(); } if ( XRM.size() > 0) { qDebug() << "\n\n\n\n Graph::clear() clearing DM\n\n\n"; XRM.clear(); } m_verticesList.clear(); m_verticesSet.clear(); m_isolatedVerticesList.clear(); m_vertexPairsNotConnected.clear(); m_vertexPairsUnilaterallyConnected.clear(); influenceDomains.clear(); influenceRanges.clear(); triadTypeFreqs.clear(); //clear relations relationsClear(); relationAdd(tr(("unnamed"))); m_fileFormat=FILE_UNRECOGNIZED; m_graphName=""; m_totalVertices=0; m_totalEdges=0; outboundEdgesVert=0; inboundEdgesVert=0; reciprocalEdgesVert=0; m_vertexClicked = 0; m_clickedEdge.v1=0; m_clickedEdge.v2=0; order=true; //returns true if the indexes of the list is ordered. m_undirected=false; m_isWeighted=false; m_symmetric=true; m_graphDensity = -1; calculatedGraphSymmetry = false; calculatedGraphWeighted = false; calculatedGraphDensity = false; calculatedEdges = false; calculatedVertices=false; calculatedVerticesList = false; calculatedVerticesSet = false; calculatedAdjacencyMatrix=false; calculatedDistances=false; calculatedIsolates = false; calculatedCentralities=false; calculatedDP=false; calculatedDC=false; calculatedIC=false; calculatedIRCC=false; calculatedPP=false; calculatedPRP=false; calculatedTriad=false; graphModifiedFlag=false; graphLoadedTerminateParserThreads("Graph::clear()"); webCrawlTerminateThreads("Graph::clear()"); qDebug()<< "Graph::clear() - m_graph cleared. Now reports size" << m_graph.size() << "emitting graphModifiedSet()"; graphModifiedSet(graphModifiedFlag,true); } /** * @brief Graph::canvasSizeSet * Called when MW and GraphicsWidget resizes to update canvasWidth and canvasHeight * @param w * @param h */ void Graph::canvasSizeSet(const int w, const int h){ float fX= (float)(w)/(float)(canvasWidth); float fY= (float)(h)/(float)(canvasHeight); float newX, newY; qDebug() << "Graph::canvasSizeSet() - new size (" << w << ", " << h<<")" << "adjusting node positions if any."; QList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ newX = (*it)->x() * fX ; newY = (*it)->y() * fY ; (*it)->setX( newX ) ; (*it)->setY( newY ); emit setNodePos((*it)->name(), newX , newY); graphModifiedSet(GRAPH_CHANGED_POSITIONS,false); } canvasWidth = w; canvasHeight= h; } /** * @brief Graph::canvasMaxRadius * @return */ double Graph::canvasMaxRadius () const { return ( canvasHeight < canvasWidth ) ? canvasHeight / 2.0 -30 : canvasWidth/2.0 - 30; } /** * @brief Graph::canvasMinDimension * @return */ float Graph::canvasMinDimension() const { return ( canvasHeight < canvasWidth ) ? canvasHeight-30 : canvasWidth-30; } /** * @brief Graph::canvasVisibleX * @param x * @return * Checks if x is visible inside the canvas usable area * and if not returns an adjusted x-coordinate */ double Graph::canvasVisibleX(const double &x) const { return qMin ( canvasWidth - 30.0 , qMax (30.0 , x ) ); } /** * @brief Graph::canvasVisibleY * @param y * @return * Checks if y is visible inside the canvas usable area * and if not returns an adjusted y-coordinate */ double Graph::canvasVisibleY(const double &y) const { return qMin ( canvasHeight - 30.0 , qMax (30.0 , y ) ); } /** * @brief Graph::canvasRandomX * @return * Returns a random x-coordinate adjusted to be visible * inside the canvas usable area */ double Graph::canvasRandomX() const { return qMin ( canvasWidth - 30.0 , qMax (30.0 , (double) (rand()%canvasWidth) ) ); } /** * @brief Graph::canvasRandomY * @return * Returns a random y-coordinate adjusted to be visible * inside the canvas usable area */ double Graph::canvasRandomY() const { return qMin ( canvasHeight - 30.0 , qMax (30.0 , (double) (rand()%canvasHeight) ) ); } /** * @brief Graph::relationSet * Changes m_curRelation to index. * If index==RAND_MAX, changes to last added relation. * Then calls Vertex::relationSet() for all enabled vertices, to disable edges * of the old relation and enable edges of the new relation * Then, if notifyMW==TRUE, it signals signalRelationChangedToGW(int), * which disables/enables the on screen edges, and * Called from MW when the user selects a relation in the combo box. * Also called from Parser * @param index int * @param notifyMW bool */ void Graph::relationSet(int index, const bool notifyMW){ qDebug() << "++ Graph::relationSet(int) to relation " << index << " current relation is " << m_curRelation ; if (m_curRelation == index ) { qDebug() << "++ Graph::relationSet(int) - same relation - END"; return; } if ( index < 0) { qDebug() << "++ Graph::relationSet(int) - negative relation - END "; return; } else if (index==RAND_MAX) { index=relations() -1; } else if (index> relations() -1) { qDebug() << "++ Graph::relationSet(int) - not existing relation - END "; return; } QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ) continue; (*it)->relationSet(index); } m_curRelation = index; if (notifyMW) { //notify MW to change combo box relation name emit signalRelationChangedToMW(m_curRelation); //notify GW to disable/enable the on screen edges. emit signalRelationChangedToGW(m_curRelation); qDebug()<<"Graph::relationSet() - emitting graphModifiedSet(GRAPH_CHANGED_EDGES)"; graphModifiedSet(GRAPH_CHANGED_EDGES); } } /** * @brief Graph::slotEditRelationPrev * Decreases the index of editRelationChangeCombo * which signals to Graph::relationSet() */ void Graph::relationPrev(){ qDebug() << "Graph::relationPrev()"; int index=m_curRelation; if (m_curRelation>0){ --index; relationSet(index); //editFilterNodesIsolatesAct->setChecked(false); } } /** * @brief Graph::slotEditRelationNext * Increases the index of editRelationChangeCombo * which signals to Graph::relationSet() */ void Graph::relationNext(){ qDebug() << "Graph::relationNext()"; int index=m_curRelation; if ( relations() >0 && index < relations() ){ ++index; relationSet(index); //editFilterNodesIsolatesAct->setChecked(false); } } /** * @brief Graph::relationAdd * Adds a new relation named relName * Called by file parser to add a new relation * Also called from MW. * emits signalRelationAddToMW * @param relName */ void Graph::relationAdd(const QString &relName, const bool &changeRelation) { qDebug() << "Graph::relationAdd() - relation name" << relName; m_relationsList << relName; // add new relation to MW combo box emit signalRelationAddToMW(relName, false); if (changeRelation) relationSet(); } /** * @brief Returns current relation index * @return int current relation index */ int Graph::relationCurrent(){ return m_curRelation; } /** * @brief Returns current relation * @return string current relation name */ QString Graph::relationCurrentName() const{ qDebug() << "Graph::relationCurrentName() -"; return m_relationsList.value(m_curRelation); } /** * @brief Graph::relationCurrentRename * @param newName */ void Graph::relationCurrentRename(const QString &newName, const bool ¬ifyMW) { if (newName.isEmpty()) { qDebug()<< "Graph::relationCurrentRename() - m_curRelation" <name() // << " appended with index= "<0) return m_graph.back()->name(); else return 0; } /** * @brief Graph::vertexNumberMin * Returns the name of the first vertex. Used by slotRemoveNode of MW * @return int */ int Graph::vertexNumberMin() { if (m_totalVertices>0) return m_graph.front()->name(); else return 0; } /** * @brief Graph::vertexRemove * Removes the vertex named Doomed from the graph * First, it removes all edges to Doomed from other vertices * Then it changes the index of all subsequent vertices inside m_graph * Finally, it removes the vertex. * @param Doomed */ void Graph::vertexRemove(long int Doomed){ qDebug() << "Graph::vertexRemove() - doomed: " << m_graph[ index[Doomed] ]->name() << " index: " << index[Doomed] << " Removing all inbound and outbound edges "; long int indexOfDoomed=index[Doomed]; //Remove links to Doomed from each other vertex QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( (*it)->hasEdgeTo(Doomed) != 0) { qDebug()<< "Graph::vertexRemove() - vertex " << (*it)->name() << " has outbound Edge to "<< Doomed << ". Removing it."; (*it)->edgeRemoveTo(Doomed) ; } if ( (*it)->hasEdgeFrom(Doomed) != 0 ) { qDebug()<< "Graph::vertexRemove() - vertex " << (*it)->name() << " has inbound Edge from "<< Doomed << ". Removing it."; (*it)->edgeRemoveFrom(Doomed); } } qDebug()<< "Graph::vertexRemove() - Finished with vertices. Update the index which maps vertices inside m_graph " ; long int prevIndex=indexOfDoomed; qDebug()<< "Graph::vertexRemove() - Updating index of all subsequent vertices "; H_Int::const_iterator it1=index.cbegin(); while (it1 != index.cend()){ if ( it1.value() > indexOfDoomed ) { prevIndex = it1.value(); qDebug() << "Graph::vertexRemove() - vertex " << it1.key() << " had prevIndex: " << prevIndex << " > indexOfDoomed " << indexOfDoomed << " Setting new index. Index size was: "<< index.size(); index.insert( it1.key(), --prevIndex) ; qDebug() << "Graph::vertexRemove() - vertex " << it1.key() << " new index: " << index.value( it1.key(), -666) << " Index size now: "<< index.size(); } else { qDebug() << "Graph::vertexRemove() " << it1.key() << " with index " << it1.value() << " < indexOfDoomed. CONTINUE"; } ++it1; } //Now remove vertex Doomed from m_graph qDebug()<< "Graph::vertexRemove() - graph vertices=size="<< vertices() << "=" << m_graph.size() << " removing vertex at index " << indexOfDoomed ; m_graph.removeAt( indexOfDoomed ) ; m_totalVertices--; qDebug()<< "Graph::vertexRemove() - Now graph vertices=size="<< vertices() << "=" << m_graph.size() << " total edges now " << edgesEnabled(); order=false; if (vertexClicked()==Doomed) vertexClickedSet(0); graphModifiedSet(GRAPH_CHANGED_VERTICES); emit eraseNode(Doomed); } /** * @brief Graph::vertexIsolateFilter * Called from filterOrphanNodes via MainWindow to filter nodes with no links * For each orphan Vertex in the Graph, emits the filterVertex() * @param filterFlag */ void Graph::vertexIsolateFilter(bool filterFlag){ qDebug() << "*** Graph::vertexIsolateFilter() " << " setting all isolate nodes to " << filterFlag; QList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( !(*it)->isIsolated() ){ continue; } else { qDebug() << "Graph::filterOrphanNodes() Vertex " << (*it)->name() << " isolate. Toggling it and emitting setVertexVisibility signal to GW..."; (*it)->setEnabled (filterFlag) ; graphModifiedSet(GRAPH_CHANGED_VERTICES); emit setVertexVisibility( (*it)-> name(), filterFlag ); } } } /** * @brief Graph::vertexIsolated * @param v1 * @return */ bool Graph::vertexIsolated(const long int &v1) const{ if ( m_graph[ index[v1] ] -> isIsolated() ) { qDebug()<<"Graph::vertexIsolated() - vertex:"<< v1 << "isolated"; return true; } qDebug()<<"Graph::vertexIsolated() - vertex:"<< v1 << "not isolated"; return false; } /** * @brief Graph::vertexExists * Checks if there is a specific vertex in the graph. * Returns the index or -1 * Complexity: O(logN) for index retrieval * @param num * @return */ int Graph::vertexExists(const long int &v1){ qDebug () << "Graph: vertexExists() v: " << v1 << " with index " << index[v1] << " named " << m_graph[ index[v1] ] ->name(); if ( m_graph[ index[v1] ] ->name() == v1) return index[v1]; else return -1; } /** * @brief Graph::vertexExists * Checks if there is a vertex with a specific label in the graph * Returns the index or -1 * Complexity: O(N) * @param label * @return index or -1 */ int Graph::vertexExists(const QString &label){ qDebug ()<<"Graph: vertexExists() - check for label "<< label.toUtf8() ; QList::const_iterator it; int i=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( (*it) ->label() == label) { // qDebug()<< "Graph: vertexExists() at pos %i" << i; return i; } i++; } return -1; } /** * @brief Graph::vertexPosSet * Called from MW/GW when node moves to update its position * @param v1 * @param x * @param y */ void Graph::vertexPosSet(const int &v1, const int &x, const int &y){ //qDebug("Graph: vertexPosSet() for %i with index %i with %i, %i", v1, index[v1], x,y); m_graph[ index[v1] ]->setX( x ); m_graph[ index[v1] ]->setY( y ); graphModifiedSet(GRAPH_CHANGED_POSITIONS,false); } /** * @brief Graph::vertexPos * @param v1 * @return */ QPointF Graph::vertexPos(const int &v1){ return m_graph[ index[v1] ]->pos(); } /** * @brief Graph::vertexClickedSet * @param v1 * Called from GW::userClickedNode(int) to update clicked vertex number and * signal signalNodeClickedInfo(node info) to MW which shows node info on the * status bar. */ void Graph::vertexClickedSet(const int &v1) { qDebug()<<"Graph::vertexClickedSet() - " << v1; m_vertexClicked = v1; if (v1 == 0) { signalNodeClickedInfo(0); } else { edgeClickedSet(0,0); signalNodeClickedInfo( v1, vertexPos(v1), vertexLabel(v1), vertexDegreeIn(v1), vertexDegreeOut(v1), ( vertices() < 500 ) ? clusteringCoefficientLocal(v1): 0 ); } } /** * @brief Graph::vertexClicked * @return int */ int Graph::vertexClicked() const { return m_vertexClicked; } /** * @brief Graph::vertexSizeInit * Initialization function * @param size */ void Graph::vertexSizeInit (const long int size) { initVertexSize=size; } /** * @brief Graph::vertexSizeSet * Changes the size.of vertex v * Called from MW Node Properties * @param v * @param size */ void Graph::vertexSizeSet(const long int &v, const int &size) { m_graph[ index[v] ]->setSize(size); graphModifiedSet(GRAPH_CHANGED_VERTICES_METADATA); emit setNodeSize(v, size); } /** * @brief Graph::vertexSize * @param v * @return int */ int Graph::vertexSize(const long &v ) { return m_graph[ index[v] ]-> size(); } /** * @brief Graph::vertexSizeAllSet * Changes the size.of all vertices * @param size */ void Graph::vertexSizeAllSet(const int size) { qDebug()<< "Graph::vertexSizeAllSet() - new size" << size; vertexSizeInit(size); QList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { (*it)->setSize(size) ; emit setNodeSize((*it)->name(), size); } } graphModifiedSet(GRAPH_CHANGED_VERTICES_METADATA); } /** * @brief Graph::vertexShapeInit * @param shape */ void Graph::vertexShapeInit(const QString shape) { initVertexShape=shape; } /** * @brief Graph::vertexShapeSet * Changes the shape.of vertex v * @param v1 * @param shape */ void Graph::vertexShapeSet(const int v1, const QString shape){ m_graph[ index[v1] ]->setShape(shape); emit setNodeShape(v1, shape); graphModifiedSet(GRAPH_CHANGED_VERTICES_METADATA); } /** * @brief Graph::vertexShape * Returns the shape of this vertex * @param v1 * @return */ QString Graph::vertexShape(const int &v1){ return m_graph[ index[v1] ]->shape(); } /** * @brief Graph::vertexShapeAllSet * Changes the shape.of all vertices * @param shape */ void Graph::vertexShapeAllSet(const QString shape) { qDebug() << "Graph::vertexShapeAllSet - shape " <::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { (*it)->setShape(shape); emit setNodeShape((*it)->name(), shape); } } graphModifiedSet(GRAPH_CHANGED_VERTICES_METADATA); } /** * @brief Graph::vertexColorSet * Changes the color of vertex v1 * @param v1 * @param color */ void Graph::vertexColorSet(const long int &v1, const QString &color){ qDebug()<< "Graph: vertexColorSet for "<< v1 << ", index " << index[v1]<< " with color "<< color; m_graph[ index[v1] ]->setColor ( color ); emit setNodeColor ( m_graph[ index[v1] ]-> name(), color ); graphModifiedSet(GRAPH_CHANGED_VERTICES_METADATA); } /** * @brief Graph::vertexColor * @param v1 * @return */ QColor Graph::vertexColor(const long int &v1){ return QColor ( m_graph[ index[v1] ] -> color() ) ; } /** * @brief Graph::vertexColorInit * default vertex color initialization * @param color */ void Graph::vertexColorInit(const QString &color){ initVertexColor=color; } /** * @brief Graph::vertexColorAllSet * Changes the color of all vertices and updates default vertex color * @param color */ void Graph::vertexColorAllSet(const QString &color) { qDebug() << "*** Graph::vertexColorAllSet() " << " to " << color; vertexColorInit(color); QList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { qDebug() << "Graph::vertexColorAllSet() Vertex " << (*it)->name() << " new color " << color; (*it)->setColor(color) ; emit setNodeColor ( (*it)-> name(), color ); } } graphModifiedSet(GRAPH_CHANGED_VERTICES_METADATA); } /** * @brief Graph::vertexNumberColorInit * Changes the initial color of vertices numbers * @param color */ void Graph::vertexNumberColorInit (QString color) { initVertexNumberColor = color; } /** * @brief Graph::vertexNumberSizeInit * Changes the initial size of vertices numbers * @param size */ void Graph::vertexNumberSizeInit (const int &size) { initVertexNumberSize = size; } /** * @brief Graph::vertexNumberSizeSet * Changes the size.of vertex v number * @param v * @param size */ void Graph::vertexNumberSizeSet(const long int &v, const int &size) { m_graph[ index[v] ]->setNumberSize (size); graphModifiedSet(GRAPH_CHANGED_MINOR_OPTIONS); } /** * @brief Graph::vertexNumberSizeSetAll * @param size */ void Graph::vertexNumberSizeSetAll(const int &size) { qDebug() << "*** Graph::vertexNumberSizeSetAll() " << " to " << size; vertexNumberSizeInit(size); QList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { qDebug() << "Graph::vertexNumberSizeSetAll() Vertex " << (*it)->name() << " new size " << size; (*it)->setNumberSize(size) ; emit setNodeNumberSize ( (*it)-> name(), size); } } graphModifiedSet(GRAPH_CHANGED_MINOR_OPTIONS); } //Changes the initial distance of vertices numbers void Graph::vertexNumberDistanceInit(const int &distance) { initVertexNumberDistance = distance; } /** * @brief Graph::vertexNumberDistanceSet * Changes the distance.of vertex v number from the vertex * @param v * @param size */ void Graph::vertexNumberDistanceSet(const long int &v, const int &newDistance) { m_graph[ index[v] ]->setNumberDistance (newDistance); graphModifiedSet(GRAPH_CHANGED_MINOR_OPTIONS); emit setNodeNumberDistance(v, newDistance); } /** * @brief Graph::vertexNumberDistanceSetAll * Changes the distance.of all vertex number from their vertices * @param size */ void Graph::vertexNumberDistanceSetAll(const int &newDistance) { qDebug() << "*** Graph::vertexNumberDistanceSetAll() " << " to " << newDistance; vertexNumberDistanceInit(newDistance); QList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { qDebug() << "Graph::vertexNumberDistanceSetAll() Vertex " << (*it)->name() << " new distance " << newDistance; (*it)->setNumberDistance(newDistance) ; emit setNodeNumberDistance ( (*it)-> name(), newDistance); } } graphModifiedSet(GRAPH_CHANGED_MINOR_OPTIONS); } /** * @brief Graph::vertexNumbersInsideNodesSet * @param toggle */ void Graph::vertexNumbersInsideNodesSet(bool toggle){ initVertexNumberInside=toggle; } /** * @brief Graph::vertexNumbersVisibilitySet * @param toggle */ void Graph::vertexNumbersVisibilitySet(bool toggle){ initVertexNumbersVisibility=toggle; } /** * @brief Graph::vertexLabelSet * Changes the label of a vertex v1 * @param v1 * @param label */ void Graph::vertexLabelSet(int v1, QString label){ qDebug()<< "Graph::vertexLabelSet() - vertex "<< v1 << "index " << index[v1] << "new label"<< label; m_graph[ index[v1] ]->setLabel ( label); emit setNodeLabel ( m_graph[ index[v1] ]-> name(), label); graphModifiedSet(GRAPH_CHANGED_VERTICES_METADATA); } /** * @brief Graph::vertexLabel * Returns the label of a vertex v1 * @param v1 * @return */ QString Graph::vertexLabel(const long int &v1){ return m_graph[ index[v1] ]->label (); } /** * @brief Graph::vertexLabelsVisibilitySet * @param toggle */ void Graph::vertexLabelsVisibilitySet(bool toggle){ initVertexLabelsVisibility=toggle; } /** * @brief Graph::vertexLabelSizeInit * Changes the default size of vertex labels * @param newSize */ void Graph::vertexLabelSizeInit(int newSize) { initVertexLabelSize = newSize; } /** * @brief Graph::vertexLabelSizeSet * Changes the label size of vertex v1 * @param v1 * @param size */ void Graph::vertexLabelSizeSet(const long int &v1, const int &size) { qDebug()<< "Graph: vertexLabelSizeSet for "<< v1 << ", index " << index[v1]<< " with size "<< size; m_graph[ index[v1] ] -> setLabelSize ( size ); emit setNodeLabelSize ( v1, size); graphModifiedSet(GRAPH_CHANGED_MINOR_OPTIONS); } /** * @brief Graph::vertexLabelSizeAllSet * Changes the label size of all vertices * @param size */ void Graph::vertexLabelSizeAllSet(const int &size) { qDebug() << "*** Graph::vertexLabelSizeAllSet() " << " to " << size; vertexLabelSizeInit(size); QList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { qDebug() << "Graph::vertexLabelSizeAllSet() Vertex " << (*it)->name() << " new size " << size; (*it)->setLabelSize(size) ; emit setNodeLabelSize ( (*it)-> name(), size); } } graphModifiedSet(GRAPH_CHANGED_MINOR_OPTIONS); } /** * @brief Graph::vertexLabelColorAllSet * Changes the label color of all vertices * @param size */ void Graph::vertexLabelColorAllSet(const QString &color) { qDebug() << "*** Graph::vertexLabelColorAllSet() " << " to " << color; vertexLabelColorInit(color); QList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { qDebug() << "Graph::vertexLabelColorAllSet() Vertex " << (*it)->name() << " new color" << color; (*it)->setLabelColor(color); emit setNodeLabelColor( (*it)-> name(), color); } } graphModifiedSet(GRAPH_CHANGED_MINOR_OPTIONS); } /** * @brief Graph::vertexLabelColorSet * Changes the label color of vertex v1 * @param v1 * @param color */ void Graph::vertexLabelColorSet(int v1, QString color){ m_graph[ index[v1] ]->setLabelColor(color); emit setNodeLabelColor(v1, color); graphModifiedSet(GRAPH_CHANGED_MINOR_OPTIONS); } /** * @brief Graph::vertexLabelColorInit * Changes the default vertex label color * @param color */ void Graph::vertexLabelColorInit(QString color){ initVertexLabelColor=color; } /** * @brief Graph::vertexLabelDistanceSet * Changes the distance.of vertex v label from the vertex * @param v * @param size */ void Graph::vertexLabelDistanceSet(const long int &v, const int &newDistance) { m_graph[ index[v] ]->setLabelDistance (newDistance); graphModifiedSet(GRAPH_CHANGED_MINOR_OPTIONS); emit setNodeLabelDistance(v, newDistance); } /** * @brief Graph::vertexLabelDistanceAllSet * Changes the distance.of all vertex labels from their vertices * @param size */ void Graph::vertexLabelDistanceAllSet(const int &newDistance) { qDebug() << "*** Graph::vertexLabelDistanceAllSet() " << " to " << newDistance; vertexLabelDistanceInit(newDistance); QList::const_iterator it; for ( it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ){ continue; } else { qDebug() << "Graph::vertexLabelDistanceAllSet() Vertex " << (*it)->name() << " new size " << newDistance; (*it)->setLabelDistance(newDistance) ; emit setNodeLabelDistance ( (*it)-> name(), newDistance); } } graphModifiedSet(GRAPH_CHANGED_MINOR_OPTIONS); } /** * @brief Graph::vertexLabelDistanceInit * Changes the default distance of vertex labels * @param distance */ void Graph::vertexLabelDistanceInit(const int &distance) { initVertexLabelDistance = distance; } /** * @brief Graph::edgeCreate * Checks if edge exists, and if the opposite edge exists * Calls edgeAdd to add the new edge to the Graph, * then emits drawEdge() which calls GraphicsWidget::drawEdge() to draw the new edge. * Called from homonymous signal of Parser class. * Also called from MW when user clicks on the "add link" button * Also called (via MW) from GW when user middle-clicks on two nodes. * @param v1 * @param v2 * @param weight * @param color * @param reciprocal * @param drawArrows * @param bezier */ void Graph::edgeCreate(const int &v1, const int &v2, const float &weight, const QString &color, const int &type, const bool &drawArrows, const bool &bezier, const QString &label, const bool &signalMW){ qDebug() <<"-- Graph::edgeCreate() - " << v1 << " -> " << v2 << " weight " << weight << " type " << type << " label " << label; // check whether there is already such an edge // (see #713617 - https://bugs.launchpad.net/socnetv/+bug/713617) if (!edgeExists(v1,v2)){ if ( type == EDGE_RECIPROCAL_UNDIRECTED ) { qDebug()<< "-- Graph::edgeCreate() - " << "Creating RECIPROCAL edge - emitting drawEdge signal to GW"; edgeAdd ( v1, v2, weight, type, label, color ); emit drawEdge(v1, v2, weight, label, color, type, drawArrows, bezier, initEdgeWeightNumbers); } else if ( edgeExists( v2, v1) ) { qDebug()<<"-- Graph::edgeCreate() - Opposite arc exists. " << " Emitting drawEdge to GW "; edgeAdd ( v1, v2, weight, EDGE_DIRECTED_OPPOSITE_EXISTS , label, color); emit drawEdge(v1, v2, weight, label, color, EDGE_DIRECTED_OPPOSITE_EXISTS, drawArrows, bezier, initEdgeWeightNumbers); m_undirected = false; } else { qDebug()<< "-- Graph::edgeCreate() - " << "Opposite arc does not exist. Emitting drawEdge to GW..."; edgeAdd ( v1, v2, weight, EDGE_DIRECTED, label, color ); emit drawEdge(v1, v2, weight, label, color, EDGE_DIRECTED, drawArrows, bezier, initEdgeWeightNumbers); m_undirected = false; m_symmetric=false; } } else { qDebug() << "-- Graph::edgeCreate() - " << "Edge " << v1 << " -> " << v2 << " declared previously (exists) - nothing to do \n\n"; } //draw new edges the same color with those of the file loaded, // on user clicks on the canvas initEdgeColor=color; graphModifiedSet(GRAPH_CHANGED_EDGES, signalMW); } /** * @brief Graph::edgeCreateWebCrawler * Called from WebCrawler when it finds an new link * Calls edgeCreate() method with initEdgeColor * @param source * @param target */ void Graph::edgeCreateWebCrawler (const int &source, const int &target){ qDebug()<< " Graph::edgeCreateWebCrawler() - from " << source << " to " << target ; float weight = 1.0; bool drawArrows=true; bool bezier=false; edgeCreate(source, target, weight, initEdgeColor, EDGE_DIRECTED, drawArrows, bezier); } /** * @brief Graph::edgeAdd * Adds an edge between v1 and v2 * @param v1 * @param v2 * @param weight * @param label * @param color * @param type */ void Graph::edgeAdd (const int &v1, const int &v2, const float &weight, const int &type, const QString &label, const QString &color) { int source=index[v1]; int target=index[v2]; qDebug()<< "Graph: edgeAdd() from vertex "<< v1 << "["<< source << "] to vertex "<< v2 << "["<< target << "] of weight "<edgeAddTo(v2, weight ); m_graph [ target ]->edgeAddFrom(v1, weight); m_graph[ source ]->setOutLinkColor(v2, color); m_graph[ source ]->setOutEdgeLabel(v2, label); if ( weight != 1 && weight!=0) { m_isWeighted=true; //not binary graph } if (type == EDGE_DIRECTED_OPPOSITE_EXISTS ){ // make existing opposite edge reciprocal } else if (type == EDGE_RECIPROCAL_UNDIRECTED){ //create opposite edge and declare both reciprocal. m_graph [ target ]->edgeAddTo(v1, weight ); m_graph [ source ]->edgeAddFrom(v2, weight); } } /** * @brief Graph::edgeRemove * Removes the edge (arc) between v1 and v2 * @param v1 * @param v2 * @param undirected if true it also removes the opposite edge */ void Graph::edgeRemove (const long int &v1, const long int &v2, const bool &removeOpposite) { qDebug ()<< "Graph::edgeRemove() - edge " << v1 << " index " << index[v1] << " ->" << v2 << " to be removed from graph"; m_graph [ index[v1] ]->edgeRemoveTo(v2); m_graph [ index[v2] ]->edgeRemoveFrom(v1); if (graphUndirected() || removeOpposite ) { // remove opposite edge m_graph [ index[v2] ]->edgeRemoveTo(v1); m_graph [ index[v1] ]->edgeRemoveFrom(v2); m_symmetric=true; } else { if ( edgeExists(v2,v1) !=0) { m_symmetric=false; } } emit eraseEdge(v1,v2); graphModifiedSet(GRAPH_CHANGED_EDGES); } /** * @brief Graph::edgeVisibilitySet * Changes the canvas visibility of an edge * Called from Vertex when edgeFilterByWeight is called * @param relation * @param source * @param target * @param visible */ void Graph::edgeVisibilitySet ( int relation, int source, int target, bool visible) { //qDebug() << "Graph: edgeVisibilitySet - emitting signal to GW"; emit setEdgeVisibility ( relation, source, target, visible); } /** * @brief Graph::edgeFilterByWeight * Called from MW::DialogEdgeFilter to filter edges over or under * a specified weight (m_threshold). * For each Vertex in the Graph, calls the homonymous method of Vertex class. * @param m_threshold * @param overThreshold */ void Graph::edgeFilterByWeight(float m_threshold, bool overThreshold){ if (overThreshold) qDebug() << "Graph: edgeFilterByWeight() over " << m_threshold ; else qDebug() << "Graph: edgeFilterByWeight() below "<< m_threshold ; QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ (*it)->edgeFilterByWeight ( m_threshold, overThreshold ); } graphModifiedSet(GRAPH_CHANGED_EDGES); emit statusMessage(tr("Edges have been filtered.")); } /** * @brief Graph::edgeFilterByRelation * Filter out all edges of a given relation * Calls the homonymous method of Vertex class. * @param relation */ void Graph::edgeFilterByRelation(int relation, bool status){ qDebug() << "Graph::edgeFilterByRelation() " ; QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ) continue; (*it)->edgeFilterByRelation ( relation, status ); } } /** * @brief Graph::edgeFilterUnilateral * Filters (enables/disables) unilateral edges in current relationship. * If toggle=true, all non-reciprocal edges are disabled, effectively making * the network symmetric. * @param toggle */ void Graph::edgeFilterUnilateral(const bool &toggle) { qDebug() << "Graph::edgeFilterUnilateral() " ; QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ (*it)->edgeFilterUnilateral ( toggle ); } graphModifiedSet(GRAPH_CHANGED_EDGES); emit statusMessage(tr("Unilateral edges have been temporarily disabled.")); } /** * @brief Graph::vertexClickedSet * @param v1 */ void Graph::edgeClickedSet(const int &v1, const int &v2) { m_clickedEdge.v1=v1; m_clickedEdge.v2=v2; if (m_clickedEdge.v1 == 0 && m_clickedEdge.v2==0) { signalEdgeClickedInfo(); } else { float weight = m_graph[ index[ m_clickedEdge.v1] ]->hasEdgeTo(m_clickedEdge.v2); bool undirected=false; if ( edgeExists(m_clickedEdge.v1,m_clickedEdge.v2, true) && graphUndirected() ) undirected=true; signalEdgeClickedInfo( m_clickedEdge.v1 ,m_clickedEdge.v2, weight, undirected); } } ClickedEdge Graph::edgeClicked() { return m_clickedEdge; } /** * @brief Graph::edgeExists * Checks if there is a (un)directed edge (arc) from v1 to v2 Complexity: O(logN) for index retrieval + O(1) for QList index retrieval + O(logN) for checking edge(v2) * @param v1 * @param v2 * @param undirected if true, check if there is an undirected edge v1<->v2 * @return zero if arc does not exist or non-zero if arc exists */ float Graph::edgeExists (const long int &v1, const long int &v2, const bool &undirected) { edgeWeightTemp = 0; edgeWeightTemp = m_graph[ index[v1] ]->hasEdgeTo(v2); if (!undirected) { qDebug() << "Graph::edgeExists() - directed" << v1 << "->" << v2 << "="<hasEdgeTo(v1); if ( edgeWeightTemp == edgeReverseWeightTemp ){ qDebug() << "Graph::edgeExists() - undirected" << v1 << "<->" << v2 << "=" <" << v2 << "= 0"; return edgeWeightTemp; } /** * @brief Graph::edgeSymmetric * Returns TRUE if (v1, v2) is symmetric. * @param v1 * @param v2 * @return */ bool Graph::edgeSymmetric(const long int &v1, const long int &v2){ qDebug("***Graph: edgeSymmetric()"); if ( ( edgeExists( v1, v2 , true) ) !=0 ) { return true; } else { return false; } } /** * @brief Graph::edgesEnabled * Returns |E| of graph - only the enabled edges * @return */ int Graph::edgesEnabled() { if ( !graphModified() && calculatedEdges ) { qDebug()<< "Graph::edgesEnabled() - Graph unchanged, edges: " << ((graphUndirected()) ? m_totalEdges / 2 : m_totalEdges); return (graphUndirected()) ? m_totalEdges / 2 : m_totalEdges; } m_totalEdges = 0; QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ m_totalEdges+=(*it)->outEdges(); } qDebug() << "Graph::edgesEnabled() - edges recounted: " << m_totalEdges; calculatedEdges = true; return (graphUndirected()) ? m_totalEdges / 2 : m_totalEdges; } /** * @brief Graph::vertexEdgesOutbound * *Returns the number of outbound edges (arcs) from vertex v1 * @param v1 * @return */ int Graph::vertexEdgesOutbound(int v1) { qDebug("Graph: vertexEdgesOutbound()"); return m_graph[ index[v1] ]->outEdges(); } /** * @brief Graph::vertexEdgesInbound * Returns the number of inbound edges (arcs) to vertex v1 * @param v1 * @return int */ int Graph::vertexEdgesInbound (int v1) { qDebug("Graph: vertexEdgesInbound()"); return m_graph[ index[v1] ]->inEdges(); } /** * @brief Graph::edgeWeightSet * Changes the weight of an edge (arc) between v1 and v2 * @param v1 * @param v2 * @param weight */ void Graph::edgeWeightSet (const long &v1, const long &v2, const float &weight, const bool &undirected) { qDebug() << "Graph::edgeWeightSet() - " << v1 << "[" << index[v1] << "] ->" << v2 << "[" << index[v2] << "]" << " = " << weight; m_graph [ index[v1] ]->changeOutEdgeWeight(v2, weight); if (undirected) { qDebug() << "Graph::edgeWeightSet() - changing opposite edge weight too"; m_graph [ index[v2] ]->changeOutEdgeWeight(v1, weight); } emit setEdgeWeight(v1,v2, weight); graphModifiedSet(GRAPH_CHANGED_EDGES); } /** * @brief Graph::edgeWeight * Returns the color qstring of the directed edge v1 -> v2 * @param v1 * @param v2 * @return */ float Graph::edgeWeight (const long &v1, const long &v2) const{ return m_graph[ index[v1] ]->hasEdgeTo(v2); } /** * @brief Graph::edgeWeightNumbersVisibilitySet * Changes the visibility of edge weight numbers * @param toggle */ void Graph::edgeWeightNumbersVisibilitySet (const bool &toggle) { initEdgeWeightNumbers = toggle; } /** * @brief Graph::edgeColorInit * Saves the default edge color * Used by random network creation methods * @param color */ void Graph::edgeColorInit(const QString &color){ initEdgeColor=color; } /** * @brief Graph::edgeColorAllSet * Changes the color of all edges. * @param color * @return */ bool Graph::edgeColorAllSet(const QString &color, const int &threshold){ qDebug()<< "Graph::edgeColorAllSet() - new color: " << color; int target=0, source=0; edgeColorInit(color); QHash *enabledOutEdges = new QHash; QHash::const_iterator it1; QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ //updateProgressDialog(++count); source = (*it)->name(); if ( ! (*it)->isEnabled() ) continue; enabledOutEdges=(*it)->outEdgesEnabledHash(); it1=enabledOutEdges->cbegin(); while ( it1!=enabledOutEdges->cend() ){ target = it1.key(); if (threshold != RAND_MAX ) { if ( it1.value() < threshold ) { qDebug() << " Graph::edgeColorAllSet() below weight threshold " << threshold << " - edge " << source << "->" << target << " new color " << color; (*it)->setOutLinkColor(target, color); emit setEdgeColor(source, target, color); } } else { qDebug() << " Graph::edgeColorAllSet() : " << source << "->" << target << " new color " << color; (*it)->setOutLinkColor(target, color); emit setEdgeColor(source, target, color); } ++it1; } } delete enabledOutEdges; graphModifiedSet(GRAPH_CHANGED_EDGES_METADATA); return true; } /** * @brief Graph::edgeColorSet * Changes the color of edge s -> t * @param v1 * @param v2 * @param color */ void Graph::edgeColorSet(const long &v1, const long &v2, const QString &color){ qDebug()<< "Graph::edgeColorSet() - "<< v1 << " -> "<< v2 <<" index ("<< index[v1]<< " -> "<setOutLinkColor(v2, color); emit setEdgeColor(v1, v2, color); if (graphSymmetric()) { m_graph[ index[v2] ]->setOutLinkColor(v1, color); emit setEdgeColor(v2, v1, color); } graphModifiedSet(GRAPH_CHANGED_EDGES_METADATA); } /** * @brief Graph::edgeColor * Returns the color qstring of the directed edge v1 -> v2 * @param v1 * @param v2 * @return */ QString Graph::edgeColor (const long &v1, const long &v2){ return m_graph[ index[v1] ]->outLinkColor(v2); } /** * @brief Graph::edgeLabelSet * Changes the label of edge v1 -> v2 * @param v1 * @param v2 * @param weight */ void Graph::edgeLabelSet (const long &v1, const long &v2, const QString &label) { qDebug() << "Graph::edgeLabelSet() " << v1 << "[" << index[v1] << "] -> " << v2 << "[" << index[v2] << "]" << " label " << label; m_graph[ index[v1] ]->setOutEdgeLabel(v2, label); emit setEdgeLabel(v1,v2, label); graphModifiedSet(GRAPH_CHANGED_EDGES_METADATA); } /** * @brief Graph::edgeLabel * Returns the label of edge v1->v2 * @param v1 * @param v2 * @return */ QString Graph::edgeLabel (const long int &v1, const long int &v2) const { return m_graph [ index[v1] ]->outEdgeLabel(v2); } /** * @brief Graph::edgeLabelsVisibilitySet * @param toggle */ void Graph::edgeLabelsVisibilitySet (const bool &toggle) { initEdgeLabels = toggle; } /** * @brief Graph::vertexDegreeOut * Returns the outDegree (sum of outbound edge weights) of vertex v1 * @param v1 * @return */ int Graph::vertexDegreeOut (int v1) { qDebug()<< "Graph: vertexDegreeOut()"; return m_graph[ index[v1] ]->degreeOut(); } /** * @brief Graph::vertexDegreeIn * Returns the inDegree (sum of inbound edge weights) of vertex v1 * @param v1 * @return */ int Graph::vertexDegreeIn (int v1) { qDebug()<< "Graph: vertexDegreeIn()"; return m_graph[ index[v1] ]-> degreeIn(); } /** * @brief Graph::vertexNeighborhoodList * @param v1 * @return QList */ QList Graph::vertexNeighborhoodList(const int &v1) { qDebug()<< "Graph::vertexNeighborhoodList()"; return m_graph[ index[v1] ]-> neighborhoodList(); } /** * @brief Graph::vertices * Returns |V| of graph * If countAll = true, returns |V| where V the set of all enabled or not vertices * If countAll = false, it skips disabled vertices * If countAll = false and dropIsolates = true, it skips both disabled and isolated vertices * @param dropIsolates * @param countAll * @return */ int Graph::vertices(const bool dropIsolates, const bool countAll) { if ( !graphModified() && m_totalVertices!=0 && calculatedVertices ) { qDebug()<< "Graph::vertices() - Graph not modified, vertices: " << m_totalVertices; return m_totalVertices; } m_totalVertices=0; QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (countAll) { ++m_totalVertices; } else { if (dropIsolates && (*it)->isIsolated()){ qDebug()<< "Graph::vertices() - isolated vertex:" <<(*it)->name(); continue; } if ( !(*it)->isEnabled()) { qDebug()<< "Graph::vertices() - disabled vertex:" <<(*it)->name(); continue; } ++m_totalVertices; } } qDebug()<< "Graph::vertices() - Graph modified, vertices: " << m_totalVertices; calculatedVertices=true; return m_totalVertices; } /** * @brief Returns a list of all isolated vertices inside the graph * Used by * Graph::graphMatrixAdjacencyCreate() * Graph::writeMatrixAdjacencyInvert() * Graph::centralityInformation() * Graph::graphConnectedness() * @return */ QList Graph::verticesListIsolated(){ if (!graphModified() && calculatedIsolates ){ qDebug()<< "Graph::verticesListIsolated() - graph not modified and " "already calculated isolates. Returning list as is:" <::const_iterator it; m_isolatedVerticesList.clear(); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ // if ( ! (*it)->isEnabled() ) // continue; if ((*it)->isIsolated()) { m_isolatedVerticesList << (*it)->name(); qDebug()<< "Graph::verticesListIsolated() - node " << (*it)->name() << " is isolated. Marking it." ; } } qDebug()<< "Graph::verticesListIsolated() - isolated vertices list computed:" < Graph::verticesList(){ qDebug()<< "Graph::verticesList()"; if (!graphModified() && !m_verticesList.isEmpty() && calculatedVerticesList ){ return m_verticesList; } QList::const_iterator it; m_verticesList.clear(); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ) continue; m_verticesList << (*it)->name(); } calculatedVerticesList = true; return m_verticesList ; } /** * @brief Returns a QSet of all vertices numbers inside the graph * @return */ QSet Graph::verticesSet(){ qDebug()<< "Graph::verticesSet()"; if (!graphModified() && !m_verticesSet.isEmpty() && calculatedVerticesSet ){ return m_verticesSet; } QList::const_iterator it; m_verticesSet.clear(); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ) continue; m_verticesSet << (*it)->name(); } calculatedVerticesSet = true; return m_verticesSet ; } /** * @brief Creates a subgraph (clique, star, cycle, line) with vertices in vList * Iff vList is empty, then fallbacks to the m_selectedVertices. * @param vList */ void Graph::verticesCreateSubgraph(QList vList, const int &type, const int ¢er) { if ( relations() == 1 && edgesEnabled()==0 ) { QString newRelationName = QString::number ( vList.size() ) + tr("-clique"); relationCurrentRename(newRelationName, true); } if (vList.isEmpty()) { vList = m_selectedVertices; } qDebug()<<"Graph::verticesCreateSubgraph() - type:" << type << "vList:" << vList; int weight; if (type == SUBGRAPH_CLIQUE) { for (int i=0; i < vList.size(); ++i ) { for (int j=i+1; j < vList.size(); ++j ) { if ( ! (weight=edgeExists( vList.value(i), vList.value(j) ) ) ) { edgeCreate(vList.value(i), vList.value(j),1.0, initEdgeColor, EDGE_RECIPROCAL_UNDIRECTED ); } else { edgeUndirectedSet(vList.value(i), vList.value(j), weight); } } } } else if (type == SUBGRAPH_STAR) { for (int j=0; j < vList.size(); ++j ) { if ( ! (weight=edgeExists( center, vList.value(j) ) ) ) { if ( center == vList.value(j)) continue; edgeCreate(center, vList.value(j),1.0, initEdgeColor, EDGE_RECIPROCAL_UNDIRECTED ); } else { edgeUndirectedSet(center, vList.value(j), weight); } } } else if (type == SUBGRAPH_CYCLE) { int j=0; for (int i=0; i < vList.size(); ++i ) { j= ( i == vList.size()-1) ? 0:i+1; if ( ! (weight=edgeExists( vList.value(i), vList.value(j) ) ) ) { edgeCreate(vList.value(i), vList.value(j),1.0, initEdgeColor, EDGE_RECIPROCAL_UNDIRECTED ); } else { edgeUndirectedSet(vList.value(i), vList.value(j), weight); } } } else if (type == SUBGRAPH_LINE) { int j=0; for (int i=0; i < vList.size(); ++i ) { if ( i == vList.size()-1 ) break; j= i+1; if ( ! (weight=edgeExists( vList.value(i), vList.value(j) ) ) ) { edgeCreate(vList.value(i), vList.value(j),1.0, initEdgeColor, EDGE_RECIPROCAL_UNDIRECTED ); } else { edgeUndirectedSet(vList.value(i), vList.value(j), weight); } } } else { return; } } /** * @brief Graph::graphModifiedSet * @param graphChangedFlag * @param signalMW */ void Graph::graphModifiedSet(const int &graphNewStatus, const bool &signalMW){ if (graphNewStatus >0 && graphNewStatus < 10){ //minor changes, i.e. vertex positions, labels, etc graphModifiedFlag = (graphModifiedFlag > 10 ) ? graphModifiedFlag : graphNewStatus ; } else { graphModifiedFlag=graphNewStatus; } if (signalMW) { qDebug()<<"Graph::graphModifiedSet() - m_symmetric " << m_symmetric << "graphModifiedFlag" << graphModifiedFlag << "Emitting signal signalGraphModified()"; emit signalGraphModified(graphModifiedFlag, graphUndirected(), vertices(), edgesEnabled(), graphDensity()); return; } qDebug()<<"Graph::graphModifiedSet() - m_symmetric " << m_symmetric << "graphModifiedFlag" << graphModifiedFlag << "Not emitting any signal to MW"; } /** * @brief Graph::graphModified * Returns true of graph is modified (edges/vertices added/removed). * else false * @return */ bool Graph::graphModified() const { qDebug() << "Graph::graphModified() - graphModifiedFlag:" << graphModifiedFlag ; return (graphModifiedFlag > 10 ) ? true: false; } /** * @brief Graph::graphSaved * @return */ bool Graph::graphSaved() const { qDebug() << "Graph::graphSaved() - graphModifiedFlag:" << graphModifiedFlag ; return (graphModifiedFlag == 0 ) ? true: false; } /** * @brief Graph::graphLoaded * @return */ bool Graph::graphLoaded() const { qDebug() << "Graph::graphLoaded() - " << (( graphFileFormat() != FILE_UNRECOGNIZED ) ? true: false ); return ( graphFileFormat() != FILE_UNRECOGNIZED ) ? true: false; } /** * @brief Gets updates on the user-selected vertices and edges from GW and emits * their counts to MW * @param selectedVertices * @param selectedEdges */ void Graph::graphSelectionChanged(const QList &selectedVertices, const QList &selectedEdges) { m_selectedVertices = selectedVertices; m_selectedEdges = selectedEdges; qDebug() << "Graph::graphSelectionChanged()" << m_selectedVertices; emit signalSelectionChanged(m_selectedVertices.size(), m_selectedEdges.size()); } /** * @brief Returns a QList of user-selected vertices * @return */ QList Graph::graphSelectedVertices() const{ return m_selectedVertices; } /** * @brief Returns count of user-selected vertices * @return */ int Graph::graphSelectedVerticesCount() const{ return m_selectedVertices.size(); } /** * @brief Returns min of user-selected vertices * @return */ int Graph::graphSelectedVerticesMin() const{ int min = RAND_MAX; foreach (int i, m_selectedVertices) { if (i < min) min = i; } return min; } /** * @brief Returns max of user-selected vertices * @return */ int Graph::graphSelectedVerticesMax() const{ int max = 0; foreach (int i, m_selectedVertices) { if (i > max ) max = i; } return max; } /** * @brief Returns a QList of user-selected edges in pair * @return */ QList Graph::graphSelectedEdges() const{ return m_selectedEdges; } /** * @brief Returns count of user-selected edges * @return */ int Graph::graphSelectedEdgesCount() const { return m_selectedEdges.size(); } /** * @brief Graph::graphDensity * Returns ratio of present edges to total possible edges. * @return */ float Graph::graphDensity() { if (!graphModified() && calculatedGraphDensity) { qDebug()<< "Graph::graphDensity() - graph not modified and" "already calculated density. Returning last value:" << m_graphDensity; return m_graphDensity; } int V=vertices(); if (V!=0 && V!=1) { m_graphDensity = (graphUndirected()) ? (float) 2* edgesEnabled() / (float)(V*(V-1.0)) : (float) edgesEnabled() / (float)(V*(V-1.0)) ; } else { m_graphDensity = 0; } calculatedGraphDensity = true; return m_graphDensity ; } /** * @brief Graph::graphWeighted * Checks if the graph is weighted (valued), i.e. if any e in |E| has value not 0 or 1 * Complexity: O(n^2) * @return */ bool Graph::graphWeighted(){ emit statusMessage(tr("Checking if the graph edges are valued. Please wait.")); if ( ! graphModified() && calculatedGraphWeighted ) { qDebug()<< "Graph::graphWeighted() - graph not modified. Return: " << m_isWeighted; return m_isWeighted; } float m_weight=0; QList::const_iterator it, it1; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ m_weight = edgeExists ( (*it1)->name(), (*it)->name() ) ; if ( m_weight != 1 && m_weight != 0 ) { qDebug()<< "Graph: graphWeighted() - true. Graph is edge-weighted."; return m_isWeighted=true; } } } calculatedGraphWeighted = true; qDebug()<< "Graph::graphWeighted() - false. Graph is not edge-weighted."; return m_isWeighted=false; } /** Returns the sum of vertices having edgesOutbound */ int Graph::verticesWithOutboundEdges(){ return outboundEdgesVert; } /** Returns the sum of vertices having edgesInbound */ int Graph::verticesWithInboundEdges(){ return inboundEdgesVert; } /** Returns the sum of vertices having reciprocal edges */ int Graph:: verticesWithReciprocalEdges(){ return reciprocalEdgesVert; } //called from Graph, when closing network, to terminate all processes //also called indirectly when wc_spider finishes void Graph::webCrawlTerminateThreads (QString reason){ qDebug() << "Graph::webCrawlTerminateThreads() - reason " << reason << "Checking if wc_spiderThread is running..."; if (wc_spiderThread.isRunning() ) { qDebug() << "Graph::webCrawlTerminateThreads() spider thread quit"; wc_spiderThread.quit(); qDebug() << "Graph::webCrawlTerminateThreads() - deleting wc_spider pointer"; delete wc_spider; wc_spider= 0; // see why here: https://goo.gl/tQxpGA emit signalNodeSizesByInDegree(true); } } //Called by MW to start a web crawler... void Graph::webCrawl( QString seed, int maxNodes, int maxRecursion, bool extLinks, bool intLinks){ relationCurrentRename(tr("web"), true); qDebug() << "Graph::webCrawl() - seed " << seed ; //WebCrawler *crawler = new WebCrawler; qDebug() << "Graph::webCrawl() Creating wc_spider & wc_parser objects"; WebCrawler_Parser *wc_parser = new WebCrawler_Parser(seed, maxNodes, maxRecursion, extLinks, intLinks); WebCrawler_Spider *wc_spider = new WebCrawler_Spider (seed, maxNodes, maxRecursion, extLinks, intLinks); qDebug() << "Graph::webCrawl() Moving parser & spider to new QThreads!"; qDebug () << " graph thread " << this->thread(); qDebug () << " wc_parser thread " << wc_parser->thread(); qDebug () << " wc_spider thread " << wc_spider->thread(); wc_parser->moveToThread(&wc_parserThread); wc_spider->moveToThread(&wc_spiderThread); qDebug () << " graph thread is " << this->thread(); qDebug () << " wc_parser thread now " << wc_parser->thread(); qDebug () << " wc_spider thread now " << wc_spider->thread(); qDebug() << "Graph::webCrawl() Connecting signals from/to parser & spider"; connect(&wc_parserThread, &QThread::finished, wc_parser, &QObject::deleteLater); connect(&wc_spiderThread, &QThread::finished, wc_spider, &QObject::deleteLater); connect(this, &Graph::operateSpider, wc_spider, &WebCrawler_Spider::get); connect(wc_parser, &WebCrawler_Parser::signalCreateNode, this, &Graph::vertexCreateAtPosRandomWithLabel); connect(wc_parser, &WebCrawler_Parser::signalCreateEdge, this, &Graph::edgeCreateWebCrawler); connect (wc_spider, &WebCrawler_Spider::finished, this, &Graph::webCrawlTerminateThreads); connect (wc_parser, &WebCrawler_Parser::finished, this, &Graph::webCrawlTerminateThreads); connect (wc_spider, &WebCrawler_Spider::parse, wc_parser, &WebCrawler_Parser::parse ); connect (wc_parser, &WebCrawler_Parser::startSpider, wc_spider, &WebCrawler_Spider::get ); qDebug() << "Graph::webCrawl() Starting parser & spider QThreads!"; wc_parserThread.start(); wc_spiderThread.start(); qDebug() << "Graph::webCrawl() Creating initial node 1, url: " << seed; vertexCreateAtPosRandomWithLabel(1, seed, false); qDebug() << "Graph::webCrawl() calling spider get() for that url!"; emit operateSpider(); qDebug("Graph::webCrawl() - reach the end - See the threads running? "); } /** * @brief Graph::graphSymmetric * Returns TRUE if the adjacency matrix of the current relation is symmetric * @return bool */ bool Graph::graphSymmetric(){ qDebug() << "Graph::graphSymmetric() "; if (!graphModified() && calculatedGraphSymmetry){ qDebug() << "Graph::graphSymmetric() - graph not modified and " "already calculated symmetry. Returning previous result: " << m_symmetric; return m_symmetric; } m_symmetric=true; int y=0, v2=0, v1=0; QHash *enabledOutEdges = new QHash; QHash::const_iterator hit; QList::const_iterator lit; for ( lit = m_graph.cbegin(); lit != m_graph.cend(); ++lit) { v1 = (*lit) -> name(); if ( ! (*lit)->isEnabled() ) continue; qDebug() << "Graph::graphSymmetric() - Graph modified! " << " Iterate over all edges of " << v1 ; enabledOutEdges=(*lit)->outEdgesEnabledHash(); hit=enabledOutEdges->cbegin(); while ( hit!=enabledOutEdges->cend() ){ v2 = hit.key(); y=index[ v2 ]; float weight = hit.value(); if ( m_graph[y]->hasEdgeTo( v1) != weight) { m_symmetric=false; // qDebug() <<"Graph::graphSymmetric() - " // << " graph not symmetric because " // << v1 << " -> " << v2 << " weight " << weight // << " differs from " << v2 << " -> " << v1 ; break; } ++hit; } } delete enabledOutEdges; qDebug() << "Graph: graphSymmetric() -" << m_symmetric; calculatedGraphSymmetry = true; return m_symmetric; } /** * @brief Graph::graphSymmetrize * Transforms the graph to symmetric (all edges reciprocal) */ void Graph::graphSymmetrize(){ qDebug()<< "Graph::graphSymmetrize"; QList::const_iterator it; int y=0, v2=0, v1=0, weight; float invertWeight=0; QHash *enabledOutEdges = new QHash; QHash::const_iterator it1; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ v1 = (*it)->name(); qDebug() << "Graph:graphSymmetrize() - iterate over edges of v1 " << v1; enabledOutEdges=(*it)->outEdgesEnabledHash(); it1=enabledOutEdges->cbegin(); while ( it1!=enabledOutEdges->cend() ){ v2 = it1.key(); weight = it1.value(); y=index[ v2 ]; qDebug() << "Graph:graphSymmetrize() - " << " v1 " << v1 << " outLinked to " << v2 << " weight " << weight; invertWeight = m_graph[y]->hasEdgeTo( v1 ) ; if ( invertWeight == 0 ) { qDebug() << "Graph:graphSymmetrize(): s = " << v1 << " is NOT inLinked from y = " << v2 ; edgeCreate( v2, v1, weight, initEdgeColor, false, true, false, QString::null, false); } else { qDebug() << "Graph: graphSymmetrize(): v1 = " << v1 << " is already inLinked from v2 = " << v2 ; if (weight!= invertWeight ) edgeWeightSet(v2,v1,weight); } ++it1; } } delete enabledOutEdges; m_symmetric=true; graphModifiedSet(GRAPH_CHANGED_EDGES); } /** * @brief Graph::graphSymmetrizeStrongTies * @param allRelations */ void Graph::graphSymmetrizeStrongTies(const bool &allRelations){ qDebug()<< "Graph::graphSymmetrizeStrongTies()" << "initial relations"<::const_iterator it; QHash *outEdgesAll = new QHash; QHash::const_iterator it1; QHash *strongTies = new QHash; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ v1 = (*it)->name(); qDebug() << "Graph::graphSymmetrizeStrongTies() - v" << v1 << "iterate over outEdges in all relations"; outEdgesAll=(*it)->outEdgesEnabledHash(allRelations); //outEdgesAllRelationsUniqueHash(); it1=outEdgesAll->cbegin(); while ( it1!=outEdgesAll->cend() ){ v2 = it1.key(); weight = it1.value(); y=index[ v2 ]; qDebug() << "Graph::graphSymmetrizeStrongTies() - " << v1 << "->" << v2 << "=" << weight << "Checking opposite."; invertWeight = m_graph[y]->hasEdgeTo( v1,allRelations ) ; if ( invertWeight == 0 ) { qDebug() << "Graph::graphSymmetrizeStrongTies() - " << v1 << "<-" << v2 << " does not exist. Weak tie. Continue." ; } else { if (!strongTies->contains(QString::number(v1)+"--"+QString::number(v2)) && !strongTies->contains(QString::number(v2)+"--"+QString::number(v1)) ){ qDebug() << "Graph::graphSymmetrizeStrongTies() - " << v1 << "--" << v2 << " exists. Strong Tie. Adding"; strongTies->insert(QString::number(v1)+"--"+QString::number(v2), 1); } else { qDebug() << "Graph::graphSymmetrizeStrongTies() - " << v1 << "--" << v2 << " exists. Strong Tie already found. Continue"; } } ++it1; } } relationAdd("Strong Ties",true); QHash::const_iterator it2; it2=strongTies->constBegin(); QStringList vertices; qDebug() << "Graph::graphSymmetrizeStrongTies() - creating strong tie edges"; while ( it2!=strongTies->constEnd() ){ vertices = it2.key().split("--"); qDebug() << "Graph::graphSymmetrizeStrongTies() - tie " < 0, where C the Cocitation Matrix. * Thus the actor pairs cited by more common neighbors will appear * with a stronger tie between them than pairs those cited by fewer * common neighbors. The resulting relation is symmetric. */ void Graph::graphCocitation(){ qDebug()<< "Graph::graphCocitation()" << "initial relations"<printMatrixConsole(true); QList::const_iterator it, it1; relationAdd("Cocitation",true); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() || ( (*it)->isIsolated() && dropIsolates) ) { continue; } v1 = (*it)->name(); j = 0; for (it1=m_graph.cbegin(); it1!=m_graph.cend(); it1++){ qDebug()<< "Graph::graphCocitation() - (i,j)" << i+1<isEnabled() || ( (*it1)->isIsolated() && dropIsolates) ) { continue; } v2 = (*it1)->name(); if (v1==v2) { j++; qDebug()<< "Graph::graphCocitation() - skipping self loop" << v1<item(i, j) ) != 0 ) { qDebug()<< "Graph::graphCocitation() - creating edge" << v1 << "<->" << v2 << "because CT(" << i+1 << "," << j+1 << ") = " << weight; edgeCreate( v1, v2, weight, initEdgeColor, EDGE_RECIPROCAL_UNDIRECTED, true, false, QString::null, false); } j++; } i++; } m_symmetric=true; graphModifiedSet(GRAPH_CHANGED_EDGES); qDebug()<< "Graph::graphCocitation()" << "final relations"<::const_iterator it; int v2=0, v1=0, weight; QHash *enabledOutEdges = new QHash; QHash::const_iterator it1; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ v1 = (*it)->name(); qDebug() << "Graph::graphUndirectedSet() - iterate over edges of v1 " << v1; enabledOutEdges=(*it)->outEdgesEnabledHash(); it1=enabledOutEdges->cbegin(); while ( it1!=enabledOutEdges->cend() ){ v2 = it1.key(); weight = it1.value(); qDebug() << "Graph::graphUndirectedSet() - " << " v1 " << v1 << " -> " << v2 << " = " << " weight " << weight; edgeUndirectedSet(v1,v2, weight); ++it1; } } delete enabledOutEdges; m_symmetric=m_undirected=true; graphModifiedSet(GRAPH_CHANGED_EDGES, signalMW); } bool Graph::graphUndirected() { return m_undirected; } /** * @brief Graph::edgeUndirectedSet * Tranforms an edge to undirected * Emits setEdgeUndirected to GW * @param v1 * @param v2 * @param weight */ void Graph::edgeUndirectedSet(const long int &v1, const long int &v2, const float &weight) { qDebug() << "Graph::edgeUndirectedSet(): " << v1 << " -> " << v2 ; int y=index[ v2 ]; float invertWeight = m_graph[y]->hasEdgeTo( v1 ) ; if ( invertWeight == 0 ) { qDebug() << "Graph::edgeUndirectedSet(): opposite " << v1 << " <- " << v2 << " does not exist - Add it to Graph." ; edgeAdd(v2,v1, weight, EDGE_DIRECTED_OPPOSITE_EXISTS, "", initEdgeColor); } else { qDebug() << "Graph::edgeUndirectedSet(): opposite " << v1 << " <- " << v2 << " exists - Checking if edge weights not equal." ; if (weight!= invertWeight ) edgeWeightSet(v2,v1,weight); } emit setEdgeUndirected(v1, v2, weight); //graphModifiedSet(GRAPH_CHANGED_EDGES); m_undirected = true; } /** * @brief Graph::graphDistanceGeodesic * Returns the graphDistanceGeodesic between nodes numbered (i-1) and (j-1) * @param i * @param j * @param considerWeights * @param inverseWeights * @return */ int Graph::graphDistanceGeodesic(const int i, const int j, const bool considerWeights, const bool inverseWeights){ qDebug() <<"Graph::graphDistanceGeodesic()"; if ( !calculatedDistances || graphModified() ) { graphMatrixDistancesCreate(false, considerWeights, inverseWeights, false); } return DM.item(index[i],index[j]); } /** * @brief Returns the diameter of the graph, aka the largest geodesic distance * between any two vertices * @param considerWeights * @param inverseWeights * @return */ int Graph::graphDiameter(const bool considerWeights, const bool inverseWeights){ qDebug () << "Graph::graphDiameter()" ; if ( !calculatedDistances || graphModified() ) { graphMatrixDistancesCreate(false, considerWeights, inverseWeights, false); } return m_graphDiameter; } /** * @brief Graph::graphDistanceGeodesicAverage * Returns the average distance of the graph * @param considerWeights * @param inverseWeights * @param dropIsolates * @return */ float Graph::graphDistanceGeodesicAverage(const bool considerWeights, const bool inverseWeights, const bool dropIsolates){ Q_UNUSED(considerWeights); Q_UNUSED(inverseWeights); if (!calculatedDistances || graphModified()) { qDebug() <<"Graph::graphDistanceGeodesicAverage() - reachability matrix not created " "or graph modified. Recomputing XRM"; emit statusMessage ( tr("Computing Average Distance: " "Need to recompute Distances. Please wait...") ); graphConnectedness(); } qDebug() <<"Graph::graphDistanceGeodesicAverage() - m_vertexPairsNotConnected " << m_vertexPairsNotConnected.count(); int N=vertices(dropIsolates);//TOFIX if (m_graphAverageDistance!=0) { if (m_vertexPairsNotConnected.count()==0) { return m_graphAverageDistance / ( N * ( N-1.0 ) ); } else { //TODO In not connected nets, it would be nice to ask the user what to do // with unconnected pairs (make M or drop (default?) return m_graphAverageDistance / m_graphGeodesicsCount; } } else return 0; } /** * @brief Graph::graphGeodesics * Returns the number of geodesics (shortest-paths) in the graph. * @return */ int Graph::graphGeodesics() { if (!calculatedDistances || graphModified()) { graphMatrixDistancesCreate(false, false,false,false); } qDebug()<< "Graph::graphGeodesics() - geodesics:" << m_graphGeodesicsCount; return m_graphGeodesicsCount; } /** * @brief Graph::graphConnectedness() * @return int: * 2: strongly connected digraph (exists path from i to j and vice versa for every i,j) * 1: connected undirected graph * 0: not connected undirected graph no isolates * -1: not connected undirected graph with isolates * -2: unilaterally connected digraph (exists path only from i to j or from j to i, not both) * -3 disconnected digraph (there are unconnected pairs, with isolates). * -4 disconnected digraph (there are unconnected pairs, no isolates). * * Used by * MW::slotConnectedness() * MW::slotAnalyzeCentralityCloseness() * MW::slotLayoutCircularByProminenceIndex(QString ) * MW::slotLayoutNodeSizesByProminenceIndex(QString ) * MW::slotLayoutLevelByProminenceIndex(QString ) * */ int Graph::graphConnectedness(const bool updateProgress) { qDebug() << "Graph::graphConnectedness() "; if (calculatedDistances && !graphModified()) { qDebug()<< "Graph::graphConnectedness() - graph unmodified. Returning:" << m_graphConnectedness; return m_graphConnectedness; } //initially if graph is undirected, assume it is connected. // if is directed, assume it is strongly connected. if ( graphUndirected() ) { m_graphConnectedness = 1; } else { m_graphConnectedness = 2; } graphMatrixDistancesCreate(false, false,false,false); int size = vertices(false,false), i=0, j=0; m_vertexPairsNotConnected.clear(); m_vertexPairsUnilaterallyConnected.clear(); // int isolatedVertices=verticesListIsolated().count(); bool isolatedVertices = false; for (i=0; i < size ; i++) { for (j=i+1; j < size ; j++) { if ( graphUndirected() ) { if ( XRM.item(i,j) == 0 ) { // not connected because there is no path connecting (i,j) m_vertexPairsNotConnected.insertMulti(i,j); //m_vertexPairsNotConnected.insertMulti(j,i); if (vertexIsolated(i+1) || vertexIsolated(j+1)) { isolatedVertices = true; } } } else { if ( XRM.item(i,j) != 0 ) { if ( XRM.item(j,i) == 0 ) { // unilaterly connected because there is only a path i -> j m_vertexPairsUnilaterallyConnected.insertMulti(i,j); //m_vertexPairsNotConnected.insertMulti(j,i); } else { //strongly connected pair } } else { if ( XRM.item(j,i) == 0 ) { // not connected because there is no path connecting (i,j) or (j,i) m_vertexPairsNotConnected.insertMulti(i,j); //m_vertexPairsNotConnected.insertMulti(j,i); if (vertexIsolated(i+1) || vertexIsolated(j+1)) { isolatedVertices = true; } } else { // unilaterly connected because there is only a path j -> i m_vertexPairsUnilaterallyConnected.insertMulti(j,i); //m_vertexPairsNotConnected.insertMulti(i,j); } } } } if (updateProgress) emit updateProgressDialog(i+1); } if ( graphUndirected() ) { if ( m_vertexPairsNotConnected.count()>0) { if (isolatedVertices) m_graphConnectedness = -1; else m_graphConnectedness = 0; } else m_graphConnectedness = 1; } else { if (m_vertexPairsNotConnected.count()>0) { if (isolatedVertices) m_graphConnectedness = -3; else m_graphConnectedness = -4; } else if (m_vertexPairsUnilaterallyConnected.count() > 0) { m_graphConnectedness = -2; } else m_graphConnectedness = 2; } return m_graphConnectedness; } /** * @brief Graph::writeMatrixDistancesPlainText * Writes the matrix of distances to a file * @param fn * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::writeMatrixDistancesPlainText (const QString &fn, const bool &considerWeights, const bool &inverseWeights, const bool &dropIsolates) { qDebug ("Graph::writeMatrixDistancesPlainText()"); if ( !calculatedDistances || graphModified() ) { emit statusMessage ( tr("Writing Distance Matrix: " "Need to recompute Distances. Please wait...") ); graphMatrixDistancesCreate(false, considerWeights, inverseWeights, dropIsolates); } qDebug ("Graph::writeMatrixDistancesPlainText() writing to file"); QFile file (fn); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fn ); return; } QTextStream outText(&file); outText.setCodec("UTF-8"); outText.setRealNumberPrecision(m_precision); outText << "-Social Network Visualizer "<< VERSION <"; outText << tr("ECCENTRICITY (e) REPORT"); outText << ""; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The eccentricity e measures how far, at most, is each " " node from every other node.
" "In a connected graph, the eccentricity e of a vertex " "is the maximum geodesic distance between that vertex and all other vertices.
" "In a disconnected graph, the eccentricity e of all vertices " "is considered to be infinite.") << "

"; outText << "

" << "" << tr("e range: ") <<"" << tr("1 ≤ e ≤ \xE2\x88\x9E") << "

"; outText << ""; outText << "" <<"" <<"" << "" <<"" <<"" <<"" << "" <<""; QList::const_iterator it; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ rowCount++; eccentr = (*it)->eccentricity(); outText << "" <<"" <<""; } outText << "
" << tr("Actor") << "" << tr("Label") << "" << tr("e") << "" << tr("%e") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << ((eccentr == 0) ? "\xE2\x88\x9E" : QString::number(eccentr) ) << "" << ((eccentr == 0) ? "\xE2\x88\x9E" : QString::number( 100* (eccentr) / sumEccentricity ) ) << "
"; if ( minEccentricity == maxEccentricity) { outText << "

" << tr("All nodes have the same eccentricity.") << "

"; } else { outText << "

"; outText << "" << tr("Max e (Graph Diameter) = ") <<"" << maxEccentricity <<" (node "<< maxNodeEccentricity << ")" << "
" << "" << tr("Min e (Graph Radius) = ") <<"" << minEccentricity <<" (node "<< minNodeEccentricity << ")" << "
" << "" << tr("e classes = ") <<"" << classesEccentricity << "

"; } outText << "

"; outText << "" << tr("e = 1 ") <<"" << tr("when a vertex is connected to all other vertices (star node).") <<"
" << "" << tr("e > 1 ") <<"" << tr("when a vertex is not directly connected to all others. " "Larger eccentricity means that the actor is farther from others.") <<"
" << "" << tr("e = \xE2\x88\x9E ") <<"" << tr("the graph of the network is disconnected.") <<"
"; outText << "

"; outText << "

 

"; outText << "

"; outText << tr("Eccentricity Report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** * @brief Computes the distance matrix, DM, with geodesic distances between all vertices: * DM(i,j)=geodesic distance between vertex i and vertex j * In the process, it also computes many other matrices and metrics: * * The so-called sigma matrix, named TM, where TM(i,j) is the number of shortest paths * from vertex i to vertex j, called sigma(i,j). * * The reachability matrix X^R where the {i,j} element is 1 if the vertices i * and j are reachable, otherwise 0. * * The Diameter of the graph, m_graphDiameter, which is the length of the longest * shortest path between every (i,j) * * The Eccentricity of every node i which is the length of the longest shortest * path from i to every other node j * * The InfluenceRange and InfluenceDomain of each node. * * The centralities for every u in V (if centralities=true): * - Betweenness: BC(u) = Sum ( sigma(i,j,u)/sigma(i,j) ) for every s,t in V * - Stress: SC(u) = Sum ( sigma(i,j) ) for every s,t in V * - Eccentricity: EC(u) = 1/maxDistance(u,t) for some t in V * - Closeness: CC(u) = 1 / Sum( DM(u,t) ) for every t in V * - Power: * @param centralities * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::graphMatrixDistancesCreate(const bool &computeCentralities, const bool &considerWeights, const bool &inverseWeights, const bool &dropIsolates) { qDebug() << "Graph::graphMatrixDistancesCreate()" << "centralities" << computeCentralities << "considerWeights:"<::const_iterator it, it1; QList::iterator it2; int w=0, u=0,s=0, i=0; float d_sw=0, d_su=0; int progressCounter=0; m_graphDiameter=0; calculatedDistances = false; m_graphAverageDistance=0; m_graphGeodesicsCount = 0; //non zero distances qDebug() << " m_graphDiameter "<< m_graphDiameter << " m_graphAverageDistance " <setBC( 0.0 ); (*it)->setSC( 0.0 ); (*it)->setEccentricity( 0.0 ); (*it)->setEC( 0.0 ); (*it)->setCC( 0.0 ); (*it)->setPC( 0.0 ); } qDebug("MAIN LOOP: for every s in V solve the Single Source Shortest Path problem..."); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { emit updateProgressDialog( ++progressCounter ); //isolates are dropped by default in the beginning // if ( ! (*it)->isEnabled() ) // continue; s=index[(*it)->name()]; qDebug() << "Source vertex s = " << (*it)->name() << " of BFS algorithm has index " << s << ". Clearing Stack ..."; if (computeCentralities){ qDebug()<< "Empty stack Stack which will return vertices in " "order of their (non increasing) distance from S ..."; //- Complexity linear O(n) while ( !Stack.empty() ) Stack.pop(); i=0; qDebug()<< "...and for each vertex: empty list Ps of predecessors"; for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1) { (*it1)->clearPs(); //initialize all sizeOfNthOrderNeighborhood to zero sizeOfNthOrderNeighborhood.insert(i, 0); i++; } } qDebug() << "PHASE 1 (SSSP): Call BFS or dijkstra for source vertex " << (*it)->name() << " index " << s << " to determine distances and geodesics from s to every vertex t" ; if (!considerWeights) BFS(s,computeCentralities, dropIsolates ); else dijkstra(s, computeCentralities, inverseWeights, dropIsolates); qDebug("***** FINISHED PHASE 1 (SSSP) BFS / DIJKSTRA ALGORITHM. " "Continuing to calculate centralities"); if (computeCentralities){ qDebug() << "Set CC for source vertex " << (*it)->name() << " with index s = " << s ; if ( (*it)->CC() != 0 ) //Closeness centrality must be inverted CC=1.0/(*it)->CC(); else CC=0; (*it)->setCC( CC ); //Check eccentricity (max geodesic distance) eccentricity = (*it)->eccentricity(); if ( eccentricity != 0 ) { //Eccentricity Centrality is the inverted Eccentricity EC=1.0 / eccentricity; } else { EC=0; eccentricity=0; } (*it)->setEC( EC ); //Set Eccentricity Centrality (*it)->setSEC( EC ); //Set std EC = EC sumEC+=EC; //set sum EC //Find min/max Eccentricity minmax( eccentricity, (*it), maxEccentricity, minEccentricity, maxNodeEccentricity, minNodeEccentricity) ; resolveClasses(eccentricity, discreteEccentricities, classesEccentricity ,(*it)->name() ); sumEccentricity+=eccentricity; qDebug()<< "PHASE 2 (ACCUMULATION): Start back propagation of dependencies." << "Set dependency delta[u]=0 on each vertex"; i=1; //used in calculating power centrality sizeOfComponent = 1; PC=0; for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ (*it1)->setDelta(0.0); //Calculate Power Centrality: In = [ 1/(N-1) ] * ( Nd1 + Nd2 * 1/2 + ... + Ndi * 1/i ) // where Ndi (sizeOfNthOrderNeighborhood) is the number of nodes at distance i from this node. //FIXME do we need to check for disabled nodes somewhere? qDebug() << " sizeOfNthOrderNeighborhood.value("<< i<<")" << sizeOfNthOrderNeighborhood.value(i); PC += ( 1.0 / (float) i ) * sizeOfNthOrderNeighborhood.value(i); // where N is the sum Nd0 + Nd1 + Nd2 + ... + Ndi, that is the amount of nodes in the same component as the current node sizeOfComponent += sizeOfNthOrderNeighborhood.value(i); i++; } (*it)->setPC( PC ); //Power Centrality is stdized already sumPC += PC; //add to temp sumSPC if ( sizeOfComponent != 1 ) SPC = ( 1.0/(sizeOfComponent-1.0) ) * PC; else SPC = 0; (*it)->setSPC( SPC ); //Set std PC sumSPC += SPC; //add to sumSPC -- used later to compute mean and variance qDebug() << "Visit all vertices in reverse order of their discovery (from s = " << s << " ) to sum dependencies. Initial Stack size has " << Stack.size(); while ( !Stack.empty() ) { w=Stack.top(); qDebug("Stack top is vertex w=%i. This is the furthest vertex from s. Popping it.", w); Stack.pop(); QList lst=m_graph[w]->Ps(); qDebug("preLOOP: Checking size of predecessors list Ps[w]... = %i ",lst.size()); qDebug("LOOP: for every other vertex u in the list of predecessors Ps[w] of w...."); if (lst.size() > 0) // just in case...do a sanity check for ( it2=lst.begin(); it2 != lst.end(); it2++ ){ u=(*it2); qDebug("Selecting Ps[w] element u=%i with delta_u=%f. sigma(u)=TM(s,u)=%f, sigma(w)=TM(s,w)=%f, delta_w=%f ", u, m_graph[u]->delta(),TM.item(s,u), TM.item(s,w), m_graph[w]->delta()); if ( TM.item(s,w) > 0) { //delta[u]=delta[u]+(1+delta[w])*(sigma[u]/sigma[w]) ; d_su=m_graph[u]->delta()+(1.0+m_graph[w]->delta() ) * ( (float)TM.item(s,u)/(float)TM.item(s,w) ); } else { d_su=m_graph[u]->delta(); qDebug("TM (s,w) zero, i.e. zero shortest path counts from s to w - using SAME DELTA for vertex u"); } qDebug("Assigning new delta d_su = %f to u = %i", d_su, u); m_graph[u]->setDelta( d_su); } qDebug()<<" Adding delta_w to BC of w"; if (w!=s) { qDebug("w!=s. For this furthest vertex we need to add its new delta %f to old BC index: %f", m_graph[w]->delta(), m_graph[w]->BC()); d_sw = m_graph[w]->BC() + m_graph[w]->delta(); qDebug("New BC = d_sw = %f", d_sw); m_graph[w]->setBC (d_sw); } } } } if (computeCentralities) { for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { if ( dropIsolates && (*it)->isIsolated() ){ qDebug() << "vertex " << (*it)->name() << " isolated, continue. "; continue; } // Compute classes and min/maxEC SEC=(*it)->SEC(); resolveClasses(SEC, discreteECs, classesEC,(*it)->name() ); minmax( SEC, (*it), maxEC, minEC, maxNodeEC, minNodeEC) ; // Compute classes and min/maxSPC SPC = (*it)->SPC(); //same as PC resolveClasses(SPC, discretePCs, classesSPC,(*it)->name() ); minmax( SPC, (*it), maxSPC, minSPC, maxNodeSPC, minNodeSPC) ; // Compute std BC, classes and min/maxSBC if (m_symmetric) { qDebug()<< "Betweenness centrality must be divided by" <<" two if the graph is undirected"; (*it)->setBC ( (*it)->BC()/2.0); } BC=(*it)->BC(); sumBC+=BC; SBC = BC/maxIndexBC; (*it)->setSBC( SBC ); resolveClasses(SBC, discreteBCs, classesSBC); sumSBC+=SBC; minmax( SBC, (*it), maxSBC, minSBC, maxNodeSBC, minNodeSBC) ; // Compute std CC, classes and min/maxSCC CC = (*it)->CC(); sumCC+=CC; SCC = maxIndexCC * CC; (*it)->setSCC ( SCC ); resolveClasses(SCC, discreteCCs, classesSCC,(*it)->name() ); sumSCC+=SCC; minmax( SCC, (*it), maxSCC, minSCC, maxNodeSCC, minNodeSCC) ; //prepare to compute stdSC SC=(*it)->SC(); if (m_symmetric){ (*it)->setSC(SC/2.0); SC=(*it)->SC(); qDebug() << "SC of " <<(*it)->name() << " divided by 2 (because the graph is symmetric) " << (*it)->SC(); } sumSC+=SC; qDebug() << "vertex " << (*it)->name() << " - " << " EC: "<< (*it)->EC() << " CC: "<< (*it)->CC() << " BC: "<< (*it)->BC() << " SC: "<< (*it)->SC() << " PC: "<< (*it)->PC(); } // calculate mean values and prepare to compute variances meanSBC = sumSBC /(float) aVertices ; varianceSBC=0; tempVarianceBC=0; meanSCC = sumSCC /(float) aVertices ; varianceSCC=0; tempVarianceCC=0; meanSPC = sumSPC /(float) aVertices ; varianceSPC=0; tempVariancePC=0; meanEC = sumEC /(float) aVertices ; varianceEC=0; tempVarianceEC=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { if ( dropIsolates && (*it)->isIsolated() ) { continue; } // Compute std SC, classes and min/maxSSC SC=(*it)->SC(); SSC=SC/sumSC; (*it)->setSSC(SSC); resolveClasses(SSC, discreteSCs, classesSSC); sumSSC+=SSC; minmax( SSC, (*it), maxSSC, minSSC, maxNodeSSC, minNodeSSC) ; //Compute numerator of groupSBC SBC=(*it)->SBC(); nomSBC +=(maxSBC - SBC ); //calculate BC variance tempVarianceBC = ( SBC - meanSBC ) ; tempVarianceBC *=tempVarianceBC; varianceSBC += tempVarianceBC; //Compute numerator of groupCC nomSCC += maxSCC- (*it)->SCC(); //calculate CC variance tempVarianceCC = ( (*it)->SCC() - meanSCC ) ; tempVarianceCC *=tempVarianceCC; varianceSCC += tempVarianceCC; //Compute numerator of groupSPC SPC=(*it)->SPC(); nomSPC +=(maxSPC - SPC ); //calculate PC variance tempVariancePC = ( (*it)->SPC() - meanSPC ) ; tempVariancePC *=tempVariancePC; varianceSPC += tempVariancePC; //calculate EC variance tempVarianceEC = ( (*it)->EC() - meanEC ) ; tempVarianceEC *=tempVarianceEC; varianceEC += tempVarianceEC; } //compute final variances varianceSBC /= (float) aVertices; varianceSCC /= (float) aVertices; varianceSPC /= (float) aVertices; varianceEC /= (float) aVertices; // calculate SC mean value and prepare to compute variance meanSSC = sumSSC /(float) aVertices ; varianceSSC=0; tempVarianceSC=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { if ( dropIsolates && (*it)->isIsolated() ){ continue; } tempVarianceSC = ( (*it)->SSC() - meanSSC ) ; tempVarianceSC *=tempVarianceSC; varianceSSC += tempVarianceSC; } //calculate final SC variance varianceSSC /= (float) aVertices; denomSPC = ( (aVertices-2.0) ) / (2.0 ); //only for connected nets if (aVertices < 3 ) denomSPC = aVertices-1.0; //what if the net is disconnected (isolates exist) ? groupSPC = nomSPC/denomSPC; denomSCC = ( ( aVertices-1.0) * (aVertices-2.0) ) / (2.0 * aVertices -3.0); if (aVertices < 3 ) denomSCC = aVertices-1.0; groupCC = nomSCC/denomSCC; //Calculate group Closeness centrality //nomSBC*=2.0; // denomSBC = (aVertices-1.0) * (aVertices-1.0) * (aVertices-2.0); denomSBC = (aVertices-1.0) ; // Wasserman&Faust - formula 5.14 groupSBC=nomSBC/denomSBC; //Calculate group Betweenness centrality calculatedCentralities=true; } } calculatedDistances=true; } /** * Breadth-First Search (BFS) method for unweighted graphs (directed or not) INPUT: a 'source' vertex with index s and a boolean computeCentralities. (Implicitly, BFS uses the m_graph structure) OUTPUT: For every vertex t: DM(s, t) is set to the distance of each t from s For every vertex t: TM(s, t) is set to the number of shortest paths between s and t Also, if computeCentralities is true then BFS does extra operations: a) For source vertex s: it calculates CC(s) as the sum of its distances from every other vertex. it calculates eccentricity(s) as the maximum distance from all other vertices. it increases sizeOfNthOrderNeighborhood [ N ] by one, to store the number of nodes at distance n from source s b) For every vertex u: it increases SC(u) by one, when it finds a new shor. path from s to t through u. appends each neighbor y of u to the list Ps, thus Ps stores all predecessors of y on all all shortest paths from s c) Each vertex u popped from Q is pushed to a stack Stack */ void Graph::BFS(const int &s, const bool &computeCentralities, const bool &dropIsolates){ Q_UNUSED(dropIsolates); int u,w, dist_u=0, temp=0, dist_w=0; int relation=0, target=0; //int weight=0; bool edgeStatus=false; H_edges::const_iterator it1; //set distance of s from s equal to 0 DM.setItem(s,s,0); //set sigma of s from s equal to 1 TM.setItem(s,s,1); // qDebug("BFS: Construct a queue Q of integers and push source vertex s=%i to Q as initial vertex", s); queue Q; // qDebug()<<"BFS: Q size "<< Q.size(); Q.push(s); qDebug("BFS: LOOP: While Q not empty "); while ( !Q.empty() ) { qDebug("BFS: Dequeue: first element of Q is u=%i", Q.front()); u=Q.front(); Q.pop(); if ( ! m_graph [ u ]->isEnabled() ) continue ; if (computeCentralities){ // qDebug("BFS: If we are to calculate centralities, we must push u=%i to global stack Stack ", u); Stack.push(u); } qDebug() << "BFS: LOOP over every edge (u,w) e E, that is all neighbors w of vertex u"; it1=m_graph [ u ] ->m_outEdges.cbegin(); while ( it1!=m_graph [ u ] -> m_outEdges.cend() ){ relation = it1.value().first; if ( relation != relationCurrent() ) { ++it1; continue; } edgeStatus=it1.value().second.second; if ( edgeStatus != true) { ++it1; continue; } target = it1.key(); // weight = it1.value().second.first; w=index[ target ]; qDebug("BFS: u=%i is connected with node %i of index w=%i. ", u, target, w); // qDebug("BFS: Start path discovery"); if ( DM.item(s, w) == RAND_MAX ) { //if distance (s,w) is infinite, w found for the first time. qDebug("BFS: first time visiting w=%i. Enqueuing w to the end of Q", w); Q.push(w); qDebug()<<"BFS: First check if distance(s,u) = -1 (aka infinite :)) and set it to zero"; dist_u=DM.item(s,u); dist_w = dist_u + 1; qDebug("BFS: Setting distance of w=%i from s=%i equal to distance(s,u) plus 1. New distance = %i",w,s, dist_w ); DM.setItem(s, w, dist_w); m_graphAverageDistance += dist_w; m_graphGeodesicsCount++; qDebug()<< "== BFS - d(" << s <<"," << w <<")=" << DM.item(s,w) << " - inserting " << w << " to inflRange J of " << s << " - and " << s << " to inflDomain I of "<< w; XRM.setItem(s,w,1); influenceRanges.insertMulti(s,w); influenceDomains.insertMulti(w,s); if (computeCentralities){ qDebug()<<"BFS: Calculate PC: store the number of nodes at distance " << dist_w << "from s"; sizeOfNthOrderNeighborhood.insert( dist_w, sizeOfNthOrderNeighborhood.value(dist_w)+1 ); qDebug()<<"BFS: Calculate CC: the sum of distances (will invert it l8r)"; m_graph [s]->setCC (m_graph [s]->CC() + dist_w); qDebug()<<"BFS: Calculate Eccentricity: the maximum distance "; if (m_graph [s]->eccentricity() < dist_w ) m_graph [s]->setEccentricity(dist_w); } // qDebug("BFS: Checking m_graphDiameter"); if ( dist_w > m_graphDiameter){ m_graphDiameter=dist_w; // qDebug() << "BFS: new m_graphDiameter = " << m_graphDiameter ; } } qDebug("BFS: Start path counting"); //Is edge (u,w) on a shortest path from s to w via u? if ( DM.item(s,w)==DM.item(s,u)+1) { temp= TM.item(s,w)+TM.item(s,u); qDebug("BFS: Found a NEW SHORTEST PATH from s=%i to w=%i via u=%i. Setting Sigma(%i, %i) = %i",s, w, u, s, w,temp); if (s!=w) TM.setItem(s,w, temp); if (computeCentralities){ qDebug("BFS/SC: If we are to calculate centralities, we must calculate SC as well"); if ( s!=w && s != u && u!=w ) { qDebug() << "BFS: setSC of u="<SC()+1; m_graph[u]->setSC(m_graph[u]->SC()+1); } else { // qDebug() << "BFS/SC: skipping setSC of u, because s=" // <SC(); qDebug() << "BFS: appending u="<< u << " to list Ps[w=" << w << "] with the predecessors of w on all shortest paths from s "; m_graph[w]->appendToPs(u); } } ++it1; } } } /** * Dijkstra's algorithm for the SSSP in weighted graphs (directed or not) * It uses a min-priority queue prQ to provide constant time lookup of the minimum * distance. The priority queue is implemented with std::priority_queue INPUT: a 'source' vertex with index s and a boolean computeCentralities. (Implicitly, the algorithm uses the m_graph structure) OUTPUT: For every vertex t: DM(s, t) is set to the distance of each t from s For every vertex t: TM(s, t) is set to the number of shortest paths between s and t Also, if computeCentralities is true then it does extra operations: a) For source vertex s: it calculates CC(s) as the sum of its distances from every other vertex. it calculates eccentricity(s) as the maximum distance from all other vertices. it increases sizeOfNthOrderNeighborhood [ N ] by one, to store the number of nodes at distance n from source s b) For every vertex u: it increases SC(u) by one, when it finds a new shor. path from s to t through u. appends each neighbor y of u to the list Ps, thus Ps stores all predecessors of y on all all shortest paths from s c) Each vertex u popped from prQ is pushed to a stack Stack */ void Graph::dijkstra(const int &s, const bool &computeCentralities, const bool &inverseWeights, const bool &dropIsolates){ Q_UNUSED(dropIsolates); int u,w,v, temp=0; int relation=0, target=0; float weight=0, dist_u=0, dist_w=0; bool edgeStatus=false; H_edges::const_iterator it1; qDebug() << "dijkstra: Construct a priority queue prQ of all vertices-distances"; priority_queue, GraphDistancesCompare> prQ; //set distance of s from s equal to 0 DM.setItem(s,s,0); //set sigma of s from s equal to 1 TM.setItem(s,s,1); QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { v=index[ (*it)->name() ]; if (v != s ){ // NOTE: DM(i,j) init to RAND_MAX already done in graphMatrixDistancesCreate // Not needed here: DM.setItem(s,v,RAND_MAX); // qDebug() << " push " << v << " to prQ with infinite distance from s"; // prQ.push(GraphDistance(v,RAND_MAX)); //TODO // Previous node in optimal path from source // previous[v] := undefined } } qDebug() << " push source " << s << " to prQ with 0 distance from s"; //crucial: without it the priority prQ would pop arbitrary node at first loop prQ.push(GraphDistance(s,0)); qDebug()<<"dijkstra: prQ size "<< prQ.size(); qDebug() << "### dijkstra: LOOP: While prQ not empty "; while ( !prQ.empty() ) { u=prQ.top().target; qDebug()<< " *** dijkstra: take u = "<< u << " from prQ which has minimum distance from s =" << s; prQ.pop(); if ( ! m_graph [ u ]->isEnabled() ) continue ; if (computeCentralities){ qDebug()<< " dijkstra: We will calculate centralities, push u ="<< u << " to global stack Stack "; Stack.push(u); } qDebug() << " *** dijkstra: LOOP over every edge ("<< u <<", w ) e E, " << "for each neighbor w of u"; it1=m_graph [ u ] ->m_outEdges.cbegin(); while ( it1!=m_graph [ u ] -> m_outEdges.cend() ) { relation = it1.value().first; if ( relation != relationCurrent() ) { ++it1; continue; } edgeStatus=it1.value().second.second; if ( edgeStatus != true) { ++it1; continue; } target = it1.key(); weight = it1.value().second.first; w=index[ target ]; qDebug()<<" -- (u, w) = ("<< u << ","<< w << ") =" << weight <<"( node"<< target << ")"; if (inverseWeights) { //only invert if user asked to do so weight = 1.0 / weight; qDebug () << " inverting weight to " << weight; } qDebug() <<" Start path discovery"; dist_u=DM.item(s,u); if (dist_u == RAND_MAX || dist_u < 0) { dist_w = RAND_MAX; qDebug() << " dist_w = RAND_MAX " << RAND_MAX; } else { dist_w = dist_u + weight; qDebug() << " dist_w = dist_u + weight = " << dist_u << "+" << weight << "=" <SC()+1; m_graph[u]->setSC(m_graph[u]->SC()+1); } else { qDebug() << " Skipping setSC of u, because s=" <SC(); qDebug() << " Appending u="<< u << " to list Ps[w =" << w << "] with the predecessors of w on all shortest paths from s "; m_graph[w]->appendToPs(u); } } else if (dist_w > 0 && dist_w < DM.item(s, w) ) { prQ.push(GraphDistance(w,dist_w)); //@FIXME: w might have been already visited? //If so, we might use QMap which is sorted (minimum) // and also provides contain() DM.setItem(s, w, dist_w); m_graphAverageDistance += dist_w; m_graphGeodesicsCount++; qDebug() << " Found new smaller SP! Set DM (s,w) = DM(" << s << ","<< w << ") = "<< dist_w << "="<< DM.item(s,w) << " m_graphAverageDistance =" << m_graphAverageDistance << "Inserting" << w << "to inflRange J of" << s << "and" << s << "to inflDomain I of"<< w; XRM.setItem(s,w,1); influenceRanges.insertMulti(s,w); influenceDomains.insertMulti(w,s); if (s!=w) { qDebug()<<" Found NEW SP from s =" << s << " to w =" << w << " via u ="<< u << " - Setting Sigma(s, w) = 1 "; TM.setItem(s,w, 1); } if (computeCentralities){ sizeOfNthOrderNeighborhood.insert( dist_w, sizeOfNthOrderNeighborhood.value(dist_w)+1 ); qDebug()<<" For PC: number of nodes at distance " << dist_w << "from s is " << sizeOfNthOrderNeighborhood.value(dist_w); m_graph [s]->setCC (m_graph [s]->CC() + dist_w); qDebug()<<" For CC: sum of distances =" << m_graph [s]->CC() << " (will invert it l8r)"; if (m_graph [s]->eccentricity() < dist_w ) m_graph [s]->setEccentricity(dist_w); qDebug()<<" For EC: max distance =" << m_graph [s]->eccentricity(); } if ( dist_w > m_graphDiameter){ m_graphDiameter=dist_w; qDebug() << " New graph diameter =" << m_graphDiameter ; } } else qDebug() << " Not a new SP"; // qDebug()<< "### dijkstra: Start path counting"; // // Is (u,w) on a shortest path from s to w via u? // if ( DM.item(s,w)==DM.item(s,u)+weight) { // temp= TM.item(s,w)+TM.item(s,u); // } ++it1; } } qDebug() << "### dijkstra: LOOP END. prQ is empty - Returning."; } /** minmax() facilitates the calculations of minimum and maximum centralities during graphMatrixDistancesCreate() */ void Graph::minmax(float C, Vertex *v, float &max, float &min, int &maxNode, int &minNode) { qDebug() << "MINMAX C = " << C << " max = " << max << " min = " << min << " name = " << v->name(); if (C > max ) { max=C; maxNode=v->name(); } if (C < min ) { min=C; minNode=v->name(); } } /** This method calculates the number of discrete centrality classes of all vertices It stores that number in a QHash type where the centrality value is the key. Called from graphMatrixDistancesCreate() */ void Graph::resolveClasses(float C, H_StrToInt &discreteClasses, int &classes){ H_StrToInt::iterator it2; it2 = discreteClasses.find(QString::number(C)); //Amort. O(1) complexity if (it2 == discreteClasses.end() ) { classes++; discreteClasses.insert(QString::number(C), classes); } } /* * Overloaded method. It only adds displaying current vertex for debugging purposes. */ void Graph::resolveClasses(float C, H_StrToInt &discreteClasses, int &classes, int vertex){ H_StrToInt::iterator it2; Q_UNUSED(vertex); it2 = discreteClasses.find(QString::number(C)); //Amort. O(1) complexity if (it2 == discreteClasses.end() ) { classes++; discreteClasses.insert(QString::number(C), classes); } } /** * @brief Graph::centralityInformation * Calculates the Information centrality of each vertex - diagonal included * Note that there is no known generalization of Stephenson&Zelen's theory * for information centrality to directional data * @param considerWeights * @param inverseWeights */ void Graph::centralityInformation(const bool considerWeights, const bool inverseWeights){ qDebug()<< "Graph::centralityInformation()"; if (calculatedIC && !graphModified()) { return; } discreteICs.clear(); sumIC=0; maxIC=0; t_sumIC=0; minIC=RAND_MAX; classesIC=0; varianceIC=0; int isolatedVertices=verticesListIsolated().count(); int i=0, j=0, n=vertices(); float m_weight=0, weightSum=1, diagonalEntriesSum=0, rowSum=0; float IC=0, SIC=0; /* Note: isolated nodes must be dropped from the AM Otherwise, the TM might be singular, therefore non-invertible. */ bool dropIsolates=true; bool symmetrize=true; graphMatrixAdjacencyCreate(dropIsolates, considerWeights, inverseWeights, symmetrize); n-=isolatedVertices; TM.resize(n, n); invM.resize(n, n); for (i=0; i::const_iterator it; i=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( (*it)->isIsolated() ) { (*it) -> setIC ( 0 ); continue; } IC= 1.0 / ( invM.item(i,i) + (diagonalEntriesSum - 2.0 * rowSum) / n ); (*it) -> setIC ( IC ); t_sumIC += IC; i++; } for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ IC = (*it)->IC(); SIC = IC / t_sumIC ; (*it)->setSIC( SIC ); sumIC+=SIC; resolveClasses(SIC, discreteICs, classesIC); minmax( SIC, (*it), maxIC, minIC, maxNodeIC, minNodeIC) ; } float x=0; meanIC = sumIC /(float) n ; varianceIC=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ x = ( (*it)->SIC() - meanIC ) ; x *=x; varianceIC += x; } varianceIC /= (float) n; calculatedIC = true; } //Writes the information centralities to a file void Graph::writeCentralityInformation(const QString fileName, const bool considerWeights, const bool inverseWeights){ QTime computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); if (graphModified() || !calculatedIC ) { emit statusMessage ( (tr("Computing IC scores...")) ); centralityInformation(considerWeights, inverseWeights); } emit statusMessage ( tr("Writing Information Centralities to file:") + fileName ); outText.setRealNumberPrecision(m_precision); int rowCount=0; int N = vertices(); outText << htmlHead; outText << "

"; outText << tr("INFORMATION CENTRALITY (IC)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The IC index, introduced by Stephenson and Zelen (1991), measures the " "information flow through all paths between actors weighted by " "strength of tie and distance.") << "
" << tr("IC' is the standardized index (IC divided by the sumIC).") << "
" << tr ("Warning: To compute this index, SocNetV drops all isolated " "nodes and symmetrizes (if needed) the adjacency matrix.
" "Read the Manual for more.") << "

"; outText << "

" << "" << tr("IC range: ") <<"" << tr("0 ≤ IC ≤ \xE2\x88\x9E") << "

"; outText << "

" << "" << tr("IC' range: ") <<"" << tr("0 ≤ IC' ≤ 1") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ rowCount++; outText <" <<"" <<""; } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("IC") << "" << tr("IC'") << "" << tr("%IC") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*it)->IC() << "" << (*it)->SIC() << "" << (100* ((*it)->SIC())) << "
"; if ( minIC == maxIC) { outText << "

" << tr("All nodes have the same IC score.") << "

"; } else { outText << "

"; outText << "" << tr("Max IC' = ") <<"" << maxIC <<" (node "<< maxNodeIC << ")" << "
" << "" << tr("Min IC' = ") <<"" << minIC <<" (node "<< minNodeIC << ")" << "
" << "" << tr("IC classes = ") <<"" << classesIC << "

"; } outText << "

"; outText << "" << tr("IC' Sum = ") <<"" << sumIC <<"
" << "" << tr("IC' Mean = ") <<"" << meanIC <<"
" << "" << tr("IC' Variance = ") <<"" << varianceIC <<"
"; outText << "

"; outText << "

"; outText << tr("GROUP INFORMATION CENTRALISATION (GIC)") << "

"; outText << "

" << tr("Since there is no way to compute Group Information Centralization,
" "you can use Variance as a general centralization index.

") << "" << tr("Variance = ") <<"" << varianceIC << "

"; outText << "

" << tr("Variance = 0, when all nodes have the same IC value, i.e. a " "complete or a circle graph).
") << tr("Larger values of variance suggest larger variability between the " "IC' values.
") <<"(Wasserman & Faust, formula 5.20, p. 197)\n\n" << "

"; outText << "

 

"; outText << "

"; outText << tr("Information Centrality report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } //Writes the eigenvector centralities to a file void Graph::writeCentralityEigenvector(const QString fileName, const bool &considerWeights, const bool &inverseWeights, const bool &dropIsolates){ QTime computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); if (graphModified() || !calculatedIC ) { emit statusMessage ( (tr("Calculating EVC scores...")) ); centralityEigenvector(considerWeights, inverseWeights,dropIsolates); } emit statusMessage ( tr("Writing Eigenvector Centralities to file:") + fileName ); outText.setRealNumberPrecision(m_precision); int rowCount=0; int N = vertices(); outText << htmlHead; outText << "

"; outText << tr("EIGENVECTOR CENTRALITY (EVC)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The Eigenvector Centrality of each node is the ith element of " "the leading eigenvector of the adjacency matrix, that is the " "eigenvector corresponding to the largest positive eigenvalue.
" "Proposed by Bonacich (1972), the Eigenvector Centrality is " "an extension of the simpler Degree Centrality because it gives " "each actor a score proportional to the scores of its neighbors. " "Thus, a node may have high EVC score if it has lots of ties or " "it has ties to other nodes with high EVC.
" "The eigenvector centralities are also known as Gould indices.") << "
" << tr("EVC' is the scaled EVC (EVC divided by max EVC).") << "
" << tr("EVC'' is the standardized index (EVC divided by the sum of all EVCs).") << "
" << "

"; outText << "

" << "" << tr("EVC range: ") <<"" << tr("0 ≤ EVC < 1 (The eigenvector has unit euclidean length) ") << "

"; outText << "

" << "" << tr("EVC' range: ") <<"" << tr("0 ≤ EVC' ≤ 1") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ rowCount++; outText <" <<"" <<""; } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("EVC") << "" << tr("EVC'") << "" << tr("EVC''") << "" << tr("%EVC'") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*it)->EVC() << "" << (*it)->SEVC() << "" << (*it)->EVC() / sumEVC << "" << (100* ((*it)->SEVC())) << "
"; if ( minEVC == maxEVC) { outText << "

" << tr("All nodes have the same EVC score.") << "

"; } else { outText << "

"; outText << "" << tr("Max EVC = ") <<"" << maxEVC <<" (node "<< maxNodeEVC << ")" << "
" << "" << tr("Min EVC = ") <<"" << minEVC <<" (node "<< minNodeEVC << ")" << "
" << "" << tr("EVC classes = ") <<"" << classesEVC << "

"; } outText << "

"; outText << "" << tr("EVC Sum = ") <<"" << sumEVC <<"
" << "" << tr("EVC Mean = ") <<"" << meanEVC <<"
" << "" << tr("EVC Variance = ") <<"" << varianceEVC <<"
"; outText << "

"; outText << "

"; outText << tr("GROUP EIGENVECTOR CENTRALISATION (GEC)") << "

"; outText << "

" << tr("Since there is no way to compute Group Eigenvector Centralization,
" "you can use Variance as a general centralization index.

") << "" << tr("Variance = ") <<"" << varianceEVC << "

"; outText << "

" << tr("Variance = 0, when all nodes have the same EVC value, i.e. a " "complete or a circle graph).
") << tr("Larger values of variance suggest larger variability between the " "EVC' values.
") << "

"; outText << "

 

"; outText << "

"; outText << tr("Eigenvector Centrality report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** * @brief Computes Eigenvector centrality * @param considerWeights * @param inverseWeights */ void Graph::centralityEigenvector(const bool &considerWeights, const bool &inverseWeights, const bool &dropIsolates) { qDebug("Graph::centralityEigenvector()"); if (!graphModified() && calculatedEVC ) { qDebug() << "Graph::centralityEigenvector() - graph not changed - returning"; return; } //float nom=0, denom=0, SEVC=0; classesEVC=0; discreteEVCs.clear(); sumEVC=0; maxEVC=0; minEVC=RAND_MAX; varianceEVC=0; meanEVC=0; QList::const_iterator it; bool symmetrize=false; bool useDegrees=false; int N = vertices(dropIsolates); float EVC[N]; emit statusMessage(tr("Computing adjacency matrix. Please wait...")); qDebug()<<"Graph::centralityEigenvector() - Create adjacency matrix AM"; graphMatrixAdjacencyCreate(dropIsolates, considerWeights, inverseWeights, symmetrize); int i = 0; if (useDegrees) { emit statusMessage(tr("Computing outDegrees. Please wait...")); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (!(*it)->isIsolated() && dropIsolates) { continue; } EVC[i] = (*it)->degreeOut(); i++; } } else { for (int i = 0 ; i < N ; i++) { EVC[i] = 1; } } emit statusMessage(tr("Computing matrix leading eigenvector. " "Please wait...")); AM.powerIteration(EVC, sumEVC, maxEVC, maxNodeEVC, minEVC, minNodeEVC, 0.0000001, 500); emit statusMessage(tr("Leading eigenvector computed. " "Analysing centralities. Please wait...")); i = 0; meanEVC = sumEVC / (float) N; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (!(*it)->isIsolated() && dropIsolates) { continue; } (*it) -> setEVC( EVC[i]); (*it) -> setSEVC( EVC[i] / maxEVC); varianceEVC += (EVC[i]-meanEVC) * (EVC[i]-meanEVC) ; i++; } varianceEVC=varianceEVC/(float) N; // group eigenvector centralization measure is // S(cmax - c(vi)) divided by the maximum value possible, // where c(vi) is the eigenvector centrality of vertex vi. } //Calculates the degree (outDegree) centrality of each vertex - diagonal included void Graph::centralityDegree(const bool &weights, const bool &dropIsolates){ qDebug("Graph::centralityDegree()"); if (!graphModified() && calculatedDC ) { qDebug() << "Graph::centralityDegree() - graph not changed - returning"; return; } float DC=0, nom=0, denom=0, SDC=0; float weight; classesSDC=0; discreteSDCs.clear(); sumSDC=0; sumDC=0; maxSDC=0; minSDC=RAND_MAX; varianceSDC=0; meanSDC=0; int N=vertices(dropIsolates); QList::const_iterator it, it1; H_StrToInt::iterator it2; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ DC=0; if (!(*it)->isIsolated()) { for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ if ( (weight=edgeExists( (*it)->name(), (*it1)->name() ) ) !=0 ) { // qDebug() << "Graph::centralityDegree() - vertex " // << (*it)->name() // << " hasEdgeTo = " << (*it1)->name(); if (weights) DC+=weight; else DC++; //check here if the matrix is symmetric - we need this below if ( edgeExists ( (*it1)->name(), (*it)->name() , true ) == 0 ) m_symmetric = false; } } } (*it) -> setDC ( DC ) ; //Set OutDegree sumDC += DC; // store sumDC (for std calc below) qDebug() << "Graph:centralityDegree() - vertex " << (*it)->name() << " has DC = " << DC ; } // Calculate std Out-Degree, min, max, classes and sumSDC for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ DC= (*it)->DC(); if (!weights) { SDC = ( DC / (N-1.0) ); } else { SDC= ( DC / (sumDC) ); } (*it) -> setSDC( SDC ); //Set Standard DC // qDebug() << "Graph::centralityDegree() - vertex " // << (*it)->name() << " SDC " << (*it)->SDC (); sumSDC+=SDC; it2 = discreteSDCs.find(QString::number(SDC)); if (it2 == discreteSDCs.end() ) { classesSDC++; // qDebug("This is a new DC class"); discreteSDCs.insert ( QString::number(SDC), classesSDC ); } //qDebug() << "DC classes = " << classesSDC; if (maxSDC < SDC ) { maxSDC = SDC ; maxNodeSDC=(*it)->name(); } if (minSDC > SDC ) { minSDC = SDC ; minNodeSDC=(*it)->name(); } } if (minSDC == maxSDC) maxNodeSDC=-1; meanSDC = sumSDC / (float) N; // qDebug() << "Graph::centralityDegree() - sumSDC " << sumSDC // << " vertices " << N << " meanSDC = sumSDC / N = " << meanSDC; // Calculate Variance and the Degree Centralisation of the whole graph. for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (dropIsolates && (*it)->isIsolated() ) { continue; } SDC= (*it)->SDC(); nom+= (maxSDC-SDC); varianceSDC += (SDC-meanSDC) * (SDC-meanSDC) ; } varianceSDC=varianceSDC/(float) N; // qDebug() << "Graph::centralityDegree() - variance = " << varianceSDC; if (m_symmetric) { // we divide by N-1 because we use std C values denom= (N-1.0)*(N-2.0) / (N-1.0); } else { denom=(N-1.0)*(N-1.0) / (N-1.0); } if (N < 3 ) denom = N-1.0; // qDebug () << "*** N is " << N << " nom " << nom << " denom is " << denom; if (!weights) { groupDC=nom/denom; } calculatedDC=true; } /** * @brief Graph::writeCentralityDegree * @param fileName * @param considerWeights * @param dropIsolates */ void Graph::writeCentralityDegree ( const QString fileName, const bool considerWeights, const bool dropIsolates) { QTime computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); qDebug()<< "Graph:: writeCentralityDegree() considerWeights " << considerWeights << " dropIsolates " <"; outText << tr("DEGREE CENTRALITY (DC) REPORT"); outText << ""; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("In undirected networks, the DC index is the sum of edges attached to a node u.
" "In directed networks, the index is the sum of outbound arcs from node u " "to all adjacent nodes (also called \"outDegree Centrality\").
" "If the network is weighted, the DC score is the sum of weights of outbound " "edges from node u to all adjacent nodes.
" "Note: To compute inDegree Centrality, use the Degree Prestige measure.") << "
" << tr("DC' is the standardized index (DC divided by N-1 (non-valued nets) or by sumDC (valued nets).") << "

"; outText << "

" << "" << tr("DC range: ") <<"" << tr("0 ≤ DC ≤ "); if (considerWeights) outText<< infinity; else outText << maxIndexDC; outText << "

"; outText << "

" << "" << tr("DC' range: ") <<"" << tr("0 ≤ DC' ≤ 1") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; QList::const_iterator it; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ rowCount++; outText <isIsolated()) { outText << "" <<"" <<""; } else { outText << "" <<"" <<""; } } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("DC") << "" << tr("DC'") << "" << tr("%DC'") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*it)->DC() << "" << (*it)->SDC() << "" << (100* ((*it)->SDC())) << "
"; if ( minSDC == maxSDC) { outText << "

" << tr("All nodes have the same DC score.") << "

"; } else { outText << "

"; outText << "" << tr("DC Sum = ") <<"" << sumDC <<"

"; outText << "

"; outText << "" << tr("Max DC' = ") <<"" << maxSDC <<" (node "<< maxNodeSDC << ")" << "
" << "" << tr("Min DC' = ") <<"" << minSDC <<" (node "<< minNodeSDC << ")" << "
" << "" << tr("DC' classes = ") <<"" << classesSDC << "

"; } outText << "

"; outText << "" << tr("DC' Sum = ") <<"" << sumSDC <<"
" << "" << tr("DC' Mean = ") <<"" << meanSDC <<"
" << "" << tr("DC' Variance = ") <<"" << varianceSDC <<"
"; outText << "

"; if (!considerWeights) { outText << "

"; outText << tr("GROUP DEGREE CENTRALISATION (GDC)") << "

"; outText << "

"; outText << "" << tr("GDC = ") <<"" << groupDC << "

"; outText << "

" << "" << tr("GDC range: ") <<"" <<" 0 ≤ GDC ≤ 1" << "

"; outText << "

" << tr("GDC = 0, when all out-degrees are equal (i.e. regular lattice).") << "
" << tr("GDC = 1, when one node completely dominates or overshadows the other nodes.") << "
" << "(Wasserman & Faust, formula 5.5, p. 177)" << "
" << "(Wasserman & Faust, p. 101)" << "

"; } else outText << "

" << tr("Because this graph is weighted, we cannot compute Group Centralization") << "
" << tr("You can use variance as a group-level centralisation measure.") << "

"; outText << "

 

"; outText << "

"; outText << tr("Degree Centrality report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** * @brief Writes the closeness centralities to a file * @param fileName * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::writeCentralityCloseness( const QString fileName, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { QTime computationTimer; computationTimer.start(); qDebug() << "Graph::writeCentralityCloseness()" << "considerWeights"<"; outText << tr("CLOSENESS CENTRALITY (CC) REPORT"); outText << ""; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The CC index is the inverted sum of geodesic distances " "from each node u to all other nodes. " ) << "
" << tr("Note: The CC index considers outbound arcs only and " "isolate nodes are dropped by default. ") << "
" << tr("Read the Manual for more.") << "
" << tr("CC' is the standardized index (CC multiplied by (N-1 minus isolates)).") << "

"; outText << "

" << "" << tr("CC range: ") <<"" << tr("0 ≤ CC ≤ ")<< 1.0/maxIndexCC << tr(" ( 1 / Number of node pairs excluding u)") << "

"; outText << "

" << "" << tr("CC' range: ") <<"" << tr("0 ≤ CC' ≤ 1 (CC'=1 when a node is the center of a star graph)") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; QList::const_iterator it; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ rowCount++; outText <isIsolated()) { outText << "" <<"" <<""; } else { outText << "" <<"" <<""; } } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("CC") << "" << tr("CC'") << "" << tr("%CC'") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*it)->CC() << "" << (*it)->SCC() << "" << (100* ((*it)->SCC())) << "
"; if ( minSCC == maxSCC) { outText << "

" << tr("All nodes have the same CC score.") << "

"; } else { outText << "

"; outText << "" << tr("CC Sum = ") <<"" << sumCC <<"

"; outText << "

"; outText << "" << tr("Max CC' = ") <<"" << maxSCC <<" (node "<< maxNodeSCC << ")" << "
" << "" << tr("Min CC' = ") <<"" << minSCC <<" (node "<< minNodeSCC << ")" << "
" << "" << tr("CC' classes = ") <<"" << classesSCC << "

"; } outText << "

"; outText << "" << tr("CC' Sum = ") <<"" << sumSCC <<"
" << "" << tr("CC' Mean = ") <<"" << meanSCC <<"
" << "" << tr("CC' Variance = ") <<"" << varianceSCC <<"
"; outText << "

"; if (!considerWeights) { outText << "

"; outText << tr("GROUP CLOSENESS CENTRALISATION (GCC)") << "

"; outText << "

"; outText << "" << tr("GCC = ") <<"" << groupCC << "

"; outText << "

" << "" << tr("GCC range: ") <<"" <<" 0 ≤ GCC ≤ 1" << "

"; outText << "

" << tr("GCC = 0, when the lengths of the geodesics are all equal, " "i.e. a complete or a circle graph.") << "
" << tr("GCC = 1, when one node has geodesics of length 1 to all the " "other nodes, and the other nodes have geodesics of length 2. " "to the remaining (N-2) nodes.") << "
" << tr("This is exactly the situation realised by a star graph.") << "
" << "(Wasserman & Faust, formula 5.9, p. 186-187)" << "

"; } else outText << "

" << tr("Because this graph is weighted, we cannot compute Group Centralization") << "
" << tr("You can use variance as a group-level centralisation measure.") << "

"; outText << "

 

"; outText << "

"; outText << tr("Closeness Centrality report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** * @brief Graph::centralityClosenessIR * Improved node-level centrality closeness index which focuses on the * influence range of each node (the set of nodes that are reachable from it) * For each node v, this index calculates the fraction of nodes in its influence * range and divides it by the average distance of those nodes from v, * ignoring nodes that are not reachable from it. * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::centralityClosenessIR(const bool considerWeights, const bool inverseWeights, const bool dropIsolates){ qDebug()<< "Graph::centralityClosenessIR()"; if (!graphModified() && calculatedIRCC ) { qDebug() << "Graph::centralityClosenessIR() - " " graph not changed - returning"; return; } if (!calculatedDistances || graphModified()) { graphMatrixDistancesCreate(false,considerWeights,inverseWeights,dropIsolates); } // calculate centralities QList::const_iterator it; float IRCC=0,SIRCC=0; float Ji=0; float dist=0; int i = 0, j = 0; float V=vertices(dropIsolates); classesIRCC=0; discreteIRCCs.clear(); sumIRCC=0; maxIRCC=0; minIRCC=vertices(dropIsolates)-1; varianceIRCC=0; meanIRCC=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ IRCC=0; Ji = 0; if (!(*it)->isIsolated()) { for ( j=0; j < DM.cols() ; j++) { i = index[(*it)->name()]; if (i == j) continue; dist = DM.item ( i , j ); if (dist != RAND_MAX ) { IRCC += dist; Ji ++; // compute |Ji| } } // qDebug()<< "Graph::centralityClosenessIR() - vertex" // << (*it)->name() // << "actors in influence range Ji" << Ji // << "actors in network"<< (V-1) // << "fraction of reachable actors |Ji|/(V-1)=" << Ji/ (V-1) // << "distance to actors in Ji" << IRCC // << "average distance to actors in Ji" << IRCC / Ji // << "IRCC = " // << Ji / (V-1) << " / " << IRCC / Ji << " = " << ( Ji / (V-1) ) / ( IRCC / Ji); // sanity check for IRCC=0 (=> node is disconnected) if (IRCC != 0) { IRCC /= Ji; IRCC = ( Ji / (V-1) ) / IRCC; } } sumIRCC += IRCC; (*it) -> setIRCC ( IRCC ) ; (*it) -> setSIRCC ( IRCC ) ; // IRCC is a ratio, already std resolveClasses(IRCC, discreteIRCCs, classesIRCC); minmax( IRCC, (*it), maxIRCC, minIRCC, maxNodeIRCC, minNodeIRCC) ; } meanIRCC = sumIRCC / (float) V; if (minIRCC == maxIRCC) maxNodeIRCC=-1; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! dropIsolates || ! (*it)->isIsolated() ) { SIRCC= (*it) -> SIRCC(); varianceIRCC += (SIRCC-meanIRCC) * (SIRCC-meanIRCC) ; } } varianceIRCC=varianceIRCC/(float) V; calculatedIRCC=true; } //Writes the "improved" closeness centrality indices to a file void Graph::writeCentralityClosenessInfluenceRange(const QString fileName, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { QTime computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); emit statusMessage ( (tr("Computing IRCC indices")) ); centralityClosenessIR(considerWeights,inverseWeights, dropIsolates); emit statusMessage ( tr("Writing IR Closeness indices to file:") + fileName ); int rowCount=0; int N = vertices(); outText << htmlHead; outText.setRealNumberPrecision(m_precision); outText << "

"; outText << tr("INFLUENCE RANGE CLOSENESS CENTRALITY (IRCC)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The IRCC index of a node u is the ratio of the fraction of nodes " "reachable by node u to the average distance of these nodes from u " "(Wasserman & Faust, formula 5.22, p. 201)
" "Thus, this metric is similar to Closeness Centrality " "but it counts only outbound distances from each actor to other reachable nodes.
" "This metric is useful for directed networks which are " "not strongly connected (thus the ordinary CC index cannot be computed).
" "In undirected networks, the IRCC has the same properties and yields " "the same results as the ordinary Closeness Centrality.
" "Read the Manual for more. ") << "
" << tr("IRCC is standardized.") << "

"; outText << "

" << "" << tr("IRCC range: ") <<"" << tr("0 ≤ IRCC ≤ 1 (IRCC is a ratio)") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; QList::const_iterator it; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ rowCount++; outText <isIsolated()) { outText << "" <<"" <<""; } else { outText << "" <<"" <<""; } } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("IRCC") << "" << tr("%IRCC'") << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*it)->IRCC() << "" << (100* ((*it)->SIRCC())) << "
"; if ( minIRCC == maxIRCC) { outText << "

" << tr("All nodes have the same IRCC score.") << "

"; } else { outText << "

"; outText << "" << tr("Max IRCC = ") <<"" << maxIRCC <<" (node "<< maxNodeIRCC << ")" << "
" << "" << tr("Min IRCC = ") <<"" << minIRCC <<" (node "<< minNodeIRCC << ")" << "
" << "" << tr("IRCC classes = ") <<"" << classesIRCC << "

"; } outText << "

"; outText << "" << tr("IRCC Sum = ") <<"" << sumIRCC <<"
" << "" << tr("IRCC Mean = ") <<"" << meanIRCC <<"
" << "" << tr("IRCC Variance = ") <<"" << varianceIRCC <<"
"; outText << "

"; outText << "

 

"; outText << "

"; outText << tr("Influence Range Closeness Centrality report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** * @brief Writes Betweeness centralities to file * @param fileName * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::writeCentralityBetweenness(const QString fileName, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { QTime computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); if (graphModified() || !calculatedCentralities ) { graphMatrixDistancesCreate(true, considerWeights, inverseWeights, dropIsolates); } else { qDebug() << "Graph::writeCentralityBetweenness() -" "No need to recompute Distances/Centralities. Writing file."; } emit statusMessage ( tr("Writing Betweenness indices to file:") + fileName ); int rowCount=0; int N = vertices(); outText << htmlHead; outText.setRealNumberPrecision(m_precision); outText << "

"; outText << tr("BETWEENESS CENTRALITY (BC)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The BC index of a node u is the sum of δ(s,t,u) for all s,t ∈ V ") << "
" << tr("where δ(s,t,u) is the ratio of all geodesics between " "s and t which run through u. ") << "
" << tr("Read the Manual for more.") << "
" << tr("BC' is the standardized index (BC divided by (N-1)(N-2)/2 in symmetric nets or (N-1)(N-2) otherwise.") << "

"; outText << "

" << "" << tr("BC range: ") <<"" << tr("0 ≤ BC ≤ ")<< maxIndexBC << tr(" (Number of pairs of nodes excluding u)") << "

"; outText << "

" << "" << tr("BC' range: ") <<"" << tr("0 ≤ BC' ≤ 1 (BC'=1 when the node falls on all geodesics)") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; QList::const_iterator it; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ rowCount++; outText <isIsolated()) { outText << "" <<"" <<""; } else { outText << "" <<"" <<""; } } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("BC") << "" << tr("BC'") << "" << tr("%BC'") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*it)->BC() << "" << (*it)->SBC() << "" << (100* ((*it)->SBC())) << "
"; if ( minSBC == maxSBC) { outText << "

" << tr("All nodes have the same BC score.") << "

"; } else { outText << "

"; outText << "" << tr("BC Sum = ") <<"" << sumBC <<"

"; outText << "

"; outText << "" << tr("Max BC' = ") <<"" << maxSBC <<" (node "<< maxNodeSBC << ")" << "
" << "" << tr("Min BC' = ") <<"" << minSBC <<" (node "<< minNodeSBC << ")" << "
" << "" << tr("BC' classes = ") <<"" << classesSBC << "

"; } outText << "

"; outText << "" << tr("BC' Sum = ") <<"" << sumSBC <<"
" << "" << tr("BC' Mean = ") <<"" << meanSBC <<"
" << "" << tr("BC' Variance = ") <<"" << varianceSBC <<"
"; outText << "

"; if (!considerWeights) { outText << "

"; outText << tr("GROUP BETWEENESS CENTRALISATION (GBC)") << "

"; outText << "

"; outText << "" << tr("GBC = ") <<"" << groupSBC << "

"; outText << "

" << "" << tr("GBC range: ") <<"" <<" 0 ≤ GBC ≤ 1" << "

"; outText << "

" << tr("GBC = 0, when all the nodes have exactly the same betweenness index.") << "
" << tr("GBC = 1, when one node falls on all other geodesics between " "all the remaining (N-1) nodes. ") << "
" << tr("This is exactly the situation realised by a star graph.") << "
" << "(Wasserman & Faust, formula 5.13, p. 192)" << "

"; } else outText << "

" << tr("Because this graph is weighted, we cannot compute Group Centralization") << "
" << tr("You can use variance as a group-level centralisation measure.") << "

"; outText << "

 

"; outText << "

"; outText << tr("Betweenness Centrality report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } //Writes the Stress centralities to a file void Graph::writeCentralityStress( const QString fileName, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { QTime computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); if (graphModified() || !calculatedCentralities ) { graphMatrixDistancesCreate(true, considerWeights, inverseWeights,dropIsolates); } else { qDebug() << " graph not modified, and centralities calculated. Returning"; } emit statusMessage ( tr("Writing Stress indices to file:") + fileName ); int rowCount=0; int N = vertices(); outText << htmlHead; outText.setRealNumberPrecision(m_precision); outText << "

"; outText << tr("STRESS CENTRALITY (SC)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The SC index of each node u is the sum of σ(s,t,u)):
" "the number of geodesics from s to t through u.") << "
" << tr("SC' is the standardized index (SC divided by sumSC).") << "

"; outText << "

" << "" << tr("SC range: ") <<"" << tr("0 ≤ SC ≤ ")<< maxIndexSC << "

"; outText << "

" << "" << tr("SC' range: ") <<"" << tr("0 ≤ SC' ≤ 1 (SC'=1 when the node falls on all geodesics)") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; QList::const_iterator it; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ rowCount++; outText <isIsolated()) { outText << "" <<"" <<""; } else { outText << "" <<"" <<""; } } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("SC") << "" << tr("SC'") << "" << tr("%SC'") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*it)->SC() << "" << (*it)->SSC() << "" << (100* ((*it)->SSC())) << "
"; if ( minSSC == maxSSC) { outText << "

" << tr("All nodes have the same SC score.") << "

"; } else { outText << "

"; outText << "" << tr("SC Sum = ") <<"" << sumSC <<"

"; outText << "

"; outText << "" << tr("Max SC' = ") <<"" << maxSSC <<" (node "<< maxNodeSSC << ")" << "
" << "" << tr("Min SC' = ") <<"" << minSSC <<" (node "<< minNodeSSC << ")" << "
" << "" << tr("BC classes = ") <<"" << classesSSC << "

"; } outText << "

"; outText << "" << tr("SC' Sum = ") <<"" << sumSSC <<"
" << "" << tr("SC' Mean = ") <<"" << meanSSC <<"
" << "" << tr("SC' Variance = ") <<"" << varianceSSC <<"
"; outText << "

"; outText << "

"; outText << tr("Stress Centrality report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** * @brief Graph::writeCentralityEccentricity * @param fileName * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::writeCentralityEccentricity(const QString fileName, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { QTime computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); if (graphModified() || !calculatedCentralities ) { graphMatrixDistancesCreate(true, considerWeights, inverseWeights,dropIsolates); } else { qDebug() << " graph not modified, and centralities calculated. Returning"; } emit statusMessage ( tr("Writing Eccentricity indices to file:") + fileName ); int rowCount=0; int N = vertices(); outText << htmlHead; outText.setRealNumberPrecision(m_precision); outText << "

"; outText << tr("ECCENTRICITY CENTRALITY (EC)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The EC index of a node u is the inverse maximum geodesic distance " "from u to all other nodes in the network.") << "
" << tr("EC is standardized.") << "

"; outText << "

" << "" << tr("EC range: ") <<"" << tr("0 ≤ EC ≤ 1 ") << tr(" (EC=1 when the actor has ties to all other nodes)") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; QList::const_iterator it; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ rowCount++; outText <isIsolated()) { outText << "" <<"" <<""; } else { outText << "" <<"" <<""; } } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("EC=EC'") << "" << tr("%EC'") << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*it)->EC() << "" << (100* ((*it)->SEC())) << "
"; if ( minEC == maxEC) { outText << "

" << tr("All nodes have the same EC score.") << "

"; } else { outText << "

"; outText << "" << tr("Max EC = ") <<"" << maxEC <<" (node "<< maxNodeEC << ")" << "
" << "" << tr("Min EC = ") <<"" << minEC <<" (node "<< minNodeEC << ")" << "
" << "" << tr("EC classes = ") <<"" << classesEC << "

"; } outText << "

"; outText << "" << tr("EC Sum = ") <<"" << sumEC <<"
" << "" << tr("EC Mean = ") <<"" << meanEC <<"
" << "" << tr("EC Variance = ") <<"" << varianceEC <<"
"; outText << "

"; outText << "

 

"; outText << "

"; outText << tr("Eccentricity Centrality report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** * @brief Graph::writeCentralityPower * @param fileName * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::writeCentralityPower(const QString fileName, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { QTime computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); if (graphModified() || !calculatedCentralities ) { graphMatrixDistancesCreate(true, considerWeights, inverseWeights, dropIsolates); } else { qDebug() << " graph not modified, and centralities calculated. Returning"; } emit statusMessage ( tr("Writing Power indices to file:") + fileName ); int rowCount=0; int N = vertices(); outText << htmlHead; outText.setRealNumberPrecision(m_precision); outText << "

"; outText << tr("POWER CENTRALITY (PC)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The PC index, introduced by Gil and Schmidt, of a node u is the sum of the sizes of all Nth-order " "neighbourhoods with weight 1/n.") << "
" << tr("PC' is the standardized index: The PC score divided by the total number " "of nodes in the same component minus 1") << "

"; outText << "

" << "" << tr("PC range: ") <<"" << tr("0 ≤ PC ≤ ")<< maxIndexPC << "

"; outText << "

" << "" << tr("PC' range: ") <<"" << tr("0 ≤ PC' ≤ 1 (PC'=1 when the node is connected to all (star).)") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; QList::const_iterator it; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ rowCount++; outText <isIsolated()) { outText << "" <<"" <<""; } else { outText << "" <<"" <<""; } } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("PC") << "" << tr("PC'") << "" << tr("%PC'") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*it)->PC() << "" << (*it)->SPC() << "" << (100* ((*it)->SPC())) << "
"; if ( minSPC == maxSPC) { outText << "

" << tr("All nodes have the same PC score.") << "

"; } else { outText << "

"; outText << "" << tr("PC Sum = ") <<"" << sumPC <<"

"; outText << "

"; outText << "" << tr("Max PC' = ") <<"" << maxSPC <<" (node "<< maxNodeSPC << ")" << "
" << "" << tr("Min PC' = ") <<"" << minSPC <<" (node "<< minNodeSPC << ")" << "
" << "" << tr("PC classes = ") <<"" << classesSPC << "

"; } outText << "

"; outText << "" << tr("PC' Sum = ") <<"" << sumSPC <<"
" << "" << tr("PC' Mean = ") <<"" << meanSPC <<"
" << "" << tr("PC' Variance = ") <<"" << varianceSPC <<"
"; outText << "

"; if (!considerWeights) { outText << "

"; outText << tr("GROUP POWER CENTRALISATION (GPC)") << "

"; outText << "

"; outText << "" << tr("GPC = ") <<"" << groupSPC << "

"; outText << "

" << "" << tr("GPC range: ") <<"" <<" 0 ≤ GPC ≤ 1" << "

"; outText << "

" << tr("GPC = 0, when all in-degrees are equal (i.e. regular lattice).") << "
" << tr("GPC = 1, when one node is linked to all other nodes (i.e. star). ") << "
" << "

"; } else outText << "

" << tr("Because this graph is weighted, we cannot compute Group Centralization") << "
" << tr("Use mean or variance instead.") << "

"; outText << "

 

"; outText << "

"; outText << tr("Power Centrality report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** * @brief Graph::prestigeDegree * Calculates Degree Prestige (in-degree) of each vertex - diagonal included * Also the mean value and the variance of the in-degrees. * @param weights * @param dropIsolates */ void Graph::prestigeDegree(const bool &weights, const bool &dropIsolates){ qDebug()<< "Graph::prestigeDegree()"; if (!graphModified() && calculatedDP ) { qDebug() << "Graph::prestigeDegree() - " " graph not changed - returning"; return; } float DP=0, SDP=0, nom=0, denom=0; float weight; classesSDP=0; sumSDP=0; sumDP=0; maxSDP=0; minSDP=vertices(dropIsolates)-1; discreteDPs.clear(); varianceSDP=0; meanSDP=0; m_symmetric = true; QList::const_iterator it; //, it1; H_StrToInt::iterator it2; int vert=vertices(dropIsolates); int v2=0, v1=0; QHash *enabledInEdges = new QHash; QHash::const_iterator hit; qDebug()<< "Graph::prestigeDegree() - vertices" << vert <<"graph modified. Recomputing..."; for ( it = m_graph.cbegin(); it != m_graph.cend(); ++it) { v1 = (*it) -> name(); qDebug()<< "Graph::prestigeDegree() - computing DP for vertex" << v1 ; DP=0; if ( ! (*it)->isEnabled() ) { qDebug()<< "Graph::prestigeDegree() - vertex disabled. Continue."; continue; } qDebug() << "Graph::prestigeDegree() - Iterate over inbound edges of " << v1 ; enabledInEdges=(*it)->inEdgesEnabledHash(); hit=enabledInEdges->cbegin(); while ( hit!=enabledInEdges->cend() ){ v2 = hit.key(); qDebug() << "Graph::prestigeDegree() - inbound edge from" << v2; if ( ! edgeExists ( v2, v1) ) { //sanity check qDebug() << "Graph::prestigeDegree() - Cannot verify inbound edge" << v2 << "CONTINUE" ; ++hit; continue; } weight = hit.value(); if (weights) DP+=weight; else DP++; if ( edgeExists ( v1, v2) != weight) { m_symmetric=false; } ++hit; } (*it) -> setDP ( DP ) ; //Set DP sumDP += DP; qDebug() << "Graph: prestigeDegree() vertex " << (*it)->name() << " DP " << DP; } // Calculate std DP, min,max, mean for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ DP= (*it)->DP(); if (!weights) { SDP=( DP / (vert-1.0) ); //Set Standard InDegree } else { SDP =( DP / (sumDP) ); } (*it) -> setSDP( SDP ); sumSDP += SDP; qDebug() << "Graph::prestigeDegree - vertex " << (*it)->name() << " DP " << DP << " SDP " << (*it)->SDP (); it2 = discreteDPs.find(QString::number(SDP)); if (it2 == discreteDPs.end() ) { classesSDP++; qDebug("This is a new DP class"); discreteDPs.insert ( QString::number(SDP), classesSDP ); } qDebug("DP classes = %i ", classesSDP); if (maxSDP < SDP ) { maxSDP = SDP ; maxNodeDP=(*it)->name(); } if (minSDP > SDP ) { minSDP = SDP ; minNodeDP=(*it)->name(); } } if (minSDP == maxSDP) maxNodeDP=-1; meanSDP = sumSDP / (float) vert; qDebug("Graph: sumSDP = %f, meanSDP = %f", sumSDP, meanSDP); // Calculate Variance and the Degree Prestigation of the whole graph. :) for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (dropIsolates && (*it)->isIsolated() ) { continue; } SDP= (*it)->SDP(); nom+= maxSDP-SDP; varianceSDP += (SDP-meanSDP) * (SDP-meanSDP) ; } varianceSDP=varianceSDP/(float) vert; if (m_symmetric) denom=(vert-1.0)*(vert-2.0); else denom=(vert-1.0)*(vert-1.0); if (vert < 3 ) denom = vert-1.0; //qDebug () << "*** vert is " << vert << " nom " << nom << " denom is " << denom; if (!weights) { groupDP=nom/denom; qDebug("Graph: varianceSDP = %f, groupDP = %f", varianceSDP, groupDP); } delete enabledInEdges; calculatedDP=true; } /** * @brief Graph::writePrestigeDegree * @param fileName * @param considerWeights * @param dropIsolates */ void Graph::writePrestigeDegree (const QString fileName, const bool considerWeights, const bool dropIsolates) { QTime computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); prestigeDegree(considerWeights, dropIsolates); int N = vertices(); float maxIndexDP=N-1.0; int rowCount=0; outText << htmlHead; outText.setRealNumberPrecision(m_precision); outText << "

"; outText << tr("DEGREE PRESTIGE (DP)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The DP index, also known as InDegree Centrality, of a node u " "is the sum of inbound edges to that node from all adjacent nodes.
" "If the network is weighted, DP is the sum of inbound arc " "weights (Indegree) to node u from all adjacent nodes. ") << "
" << tr("DP' is the standardized index (DP divided by N-1).") << "

"; outText << "

" << "" << tr("DP range: ") <<"" << tr("0 ≤ DP ≤ "); if (considerWeights) outText<< infinity; else outText << maxIndexDP; outText << "

"; outText << "

" << "" << tr("DP' range: ") <<"" << tr("0 ≤ DP' ≤ 1") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; QList::const_iterator it; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ rowCount++; outText <isIsolated()) { outText << "" <<"" <<""; } else { outText << "" <<"" <<""; } } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("DP") << "" << tr("DP'") << "" << tr("%DP'") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*it)->DP() << "" << (*it)->SDP() << "" << (100* ((*it)->SDP())) << "
"; if ( minSDP == maxSDP) { outText << "

" << tr("All nodes have the same DP score.") << "

"; } else { outText << "

"; outText << "" << tr("DP Sum = ") <<"" << sumDP << "

"; outText << "

" << "" << tr("Max DP' = ") <<"" << maxSDP <<" (node "<< maxNodeDP << ")" << "
" << "" << tr("Min DP' = ") <<"" << minSDP <<" (node "<< minNodeDP << ")" << "
" << "" << tr("DP' classes = ") <<"" << classesSDP << "

"; } outText << "

"; outText << "" << tr("DP' Sum = ") <<"" << sumSDP <<"
" << "" << tr("DP' Mean = ") <<"" << meanSDP <<"
" << "" << tr("DP' Variance = ") <<"" << varianceSDP <<"
"; outText << "

"; if (!considerWeights) { outText << "

"; outText << tr("GROUP DEGREE PRESTIGE (GDP)") << "

"; outText << "

"; outText << "" << tr("GDP = ") <<"" << groupDP << "

"; outText << "

" << "" << tr("GDP range: ") <<"" <<" 0 ≤ GDP ≤ 1" << "

"; outText << "

" << tr("GDP = 0, when all in-degrees are equal (i.e. regular lattice).") << "
" << tr("GDP = 1, when one node is chosen by all other nodes (i.e. star). ") << "
" << tr("This is exactly the situation realised by a star graph.") << "
" << "(Wasserman & Faust, p. 203)" << "

"; } else outText << "

" << tr("Because this graph is weighted, we cannot compute Group Centralization") << "
" << tr("You can use variance as a group-level centralisation measure.") << "

"; outText << "

 

"; outText << "

"; outText << tr("Degree Prestige report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** * @brief Graph::prestigeProximity * Calculates Proximity Prestige of each vertex * Also the mean value and the variance of it.. */ void Graph::prestigeProximity( const bool considerWeights, const bool inverseWeights, const bool dropIsolates){ qDebug()<< "Graph::prestigeProximity()"; if (!graphModified() && calculatedPP ) { qDebug() << "Graph::prestigeProximity() - " " graph not changed - returning"; return; } if (!calculatedDistances || graphModified()) { graphMatrixDistancesCreate(false,considerWeights, inverseWeights,inverseWeights); } // calculate centralities QList::const_iterator it; float PP=0; float dist=0; float Ii=0; int i=0, j=0; float V=vertices(dropIsolates); classesPP=0; discretePPs.clear(); sumPP=0; maxPP=0; minPP=vertices(dropIsolates)-1; variancePP=0; meanPP=0; H_StrToInt::iterator it2; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ PP=0; i=0; Ii = 0; if (!(*it)->isIsolated()){ j = index[(*it)->name()]; for (i=0; i < DM.rows() ; i++) { if (i == j) continue; dist = DM.item ( i , j ); if (dist != RAND_MAX ) { PP += dist; Ii ++; // compute |Ii| } } qDebug()<< "Graph::prestigeProximity() - vertex" << (*it)->name() << "actors in influence domain Ii" << Ii << "actors in network"<< (V-1) << "fraction of actors who reach i |Ii|/(V-1)=" << Ii/ (V-1) << "distance to actors in Ii" << PP << "average distance to actors in Ii" << PP/ Ii << "PP= " << Ii / (V-1) << " / " << PP / Ii << " = " << ( Ii / (V-1) ) / ( PP / Ii ); // sanity check for PP=0 (=> node is disconnected) if (PP != 0) { PP /= Ii; PP = ( Ii / (V-1) ) / PP; } sumPP += PP; } (*it) -> setPP ( PP ) ; (*it) -> setSPP ( PP ) ; // PP is already stdized it2 = discretePPs.find(QString::number(PP)); if (it2 == discretePPs.end() ) { classesPP++; qDebug() << "PP = " << (*it) -> PP() << " - this is a new PP class" ; discretePPs.insert ( QString::number(PP), classesPP ); } //qDebug("PP classes = %i ", classesPP); if (maxPP < PP ) { maxPP = PP ; maxNodePP=(*it)->name(); } if (minPP > PP ) { minPP = PP ; minNodePP=(*it)->name(); } } if (minPP == maxPP) maxNodePP=-1; meanPP = sumPP / (float) V; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if (dropIsolates && (*it)->isIsolated() ) { continue; } PP= (*it) -> PP(); variancePP += (PP-meanPP) * (PP-meanPP) ; } variancePP=variancePP/(float) V; qDebug() << "Graph::prestigeProximity - sumPP = " << sumPP << " meanPP = " << meanPP << " variancePP " << variancePP; calculatedPP=true; } /** * @brief Graph::writePrestigeProximity * Writes the proximity prestige indices to a file * @param fileName * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::writePrestigeProximity( const QString fileName, const bool considerWeights, const bool inverseWeights, const bool dropIsolates) { QTime computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); emit statusMessage ( (tr("Calculating prestige proximity indices")) ); prestigeProximity(considerWeights, inverseWeights, dropIsolates); emit statusMessage ( tr("Writing Proximity Prestige indices to file:") + fileName ); int rowCount=0; int N = vertices(); outText << htmlHead; outText.setRealNumberPrecision(m_precision); outText << "

"; outText << tr("PROXIMITY PRESTIGE (PP)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The PP index of a node u is the ratio of the proportion of " "nodes who can reach u to the average distance these nodes are from u " "(Wasserman & Faust, formula 5.25, p. 204)
" "Thus, it is similar to Closeness Centrality but it counts " "only inbound distances to each actor, thus it is a measure of actor prestige.
" "This metric is useful for directed networks which are " "not strongly connected (thus the ordinary CC index cannot be computed).
" "In undirected networks, the PP has the same properties and yields " "the same results as Closeness Centrality.
" "Read the Manual for more.
") << "

"; outText << "

" << "" << tr("PP range: ") <<"" << tr("0 ≤ PP ≤ 1 (PP is a ratio)") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; QList::const_iterator it; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ rowCount++; outText <isIsolated()) { outText << "" <<"" <<""; } else { outText << "" <<"" <<""; } } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("PP=PP'") << "" << tr("%PP") << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*it)->PP() << "" << (100* ((*it)->SPP())) << "
"; if ( minPP == maxPP) { outText << "

" << tr("All nodes have the same PP score.") << "

"; } else { outText << "

"; outText << "" << tr("Max PP = ") <<"" << maxPP <<" (node "<< maxNodePP << ")" << "
" << "" << tr("Min PP = ") <<"" << minPP <<" (node "<< minNodePP << ")" << "
" << "" << tr("PP classes = ") <<"" << classesPP << "

"; } outText << "

"; outText << "" << tr("PP Sum = ") <<"" << sumPP <<"
" << "" << tr("PP Mean = ") <<"" << meanPP <<"
" << "" << tr("PP Variance = ") <<"" << variancePP <<"
"; outText << "

"; outText << "

 

"; outText << "

"; outText << tr("Proximity Prestige report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** * @brief Graph::prestigePageRank * Calculates the PageRank Prestige of each vertex * @param dropIsolates */ void Graph::prestigePageRank(const bool &dropIsolates){ qDebug()<< "Graph::prestigePageRank()"; if (! graphModified() && calculatedPRP ) { qDebug() << " graph not changed - return "; return; } discretePRPs.clear(); sumPRP=0; t_sumPRP=0; maxPRP=0; minPRP=RAND_MAX; classesPRP=0; variancePRP=0; // The parameter d is a damping factor which can be set between 0 and 1. // Google creators set d to 0.85. d_factor = 0.85; float PRP=0, oldPRP = 0; float SPRP=0; int iterations = 1; // a counter int referrer; float delta = 0.00001; // The delta where we will stop the iterative calculation float maxDelta = RAND_MAX; float sumInLinksPR = 0; // temp var for inlinks sum PR float transferedPRP = 0; float inLinks = 0; // temp var float outLinks = 0; // temp var float t_variance=0; float aVert = vertices(dropIsolates) ; QList::const_iterator it; int relation=0; bool edgeStatus=false; H_edges::const_iterator jt; qDebug()<< "Graph::prestigePageRank() " << "active vertices: " << aVert << " total vertices: " << vertices(); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ // At first, PR scores have probability distribution // from 0 to 1, so each one is set to 1/N (*it)->setPRP( 1.0 / aVert ); // compute inEdges() to warm up inEdgesConst for everyone inLinks = (*it)->inEdges(); outLinks = (*it)->outEdges(); qDebug() << "Graph::prestigePageRank() - node " << (*it)->name() << " PR = " << (*it)->PRP() << " inLinks (set const): " << inLinks << " outLinks (set const): " << outLinks; } if ( edgesEnabled() == 0 ) { qDebug()<< "Graph::prestigePageRank() " <<" - all vertices are isolated and of equal PR. Stop"; return; } // begin iteration - continue until we reach our desired delta while (maxDelta > delta) { qDebug()<< "Graph::prestigePageRank() - ITERATION : " << iterations; sumPRP=0; maxDelta = 0; maxPRP=0; minPRP=RAND_MAX; maxNodePRP = 0; minNodePRP = 0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { sumInLinksPR = 0; oldPRP = (*it)->PRP(); qDebug() << "Graph::prestigePageRank() - computing PR for node: " << (*it)->name() << " current PR " << oldPRP; if ( (*it)->isIsolated() ) { // isolates have constant PR = 1/N qDebug() << "Graph::prestigePageRank() - isolated - CONTINUE "; continue; } jt=(*it)->m_inEdges.cbegin(); qDebug() << "Graph::prestigePageRank() - Iterate over inEdges of " << (*it)->name() ; while ( jt != (*it) -> m_inEdges.cend() ) { relation = jt.value().first; if ( relation != relationCurrent() ){ ++jt; continue; } edgeStatus=jt.value().second.second; if ( edgeStatus != true){ ++jt; continue; } referrer = jt.key(); qDebug() << "Graph::prestigePageRank() - Node " << (*it)->name() << " inLinked from neighbor " << referrer << " index " << index[referrer]; if ( edgeExists( referrer , (*it)->name() ) ) { inLinks = m_graph[ index[referrer] ] ->inEdgesConst(); outLinks = m_graph[ index[referrer] ]-> outEdgesConst(); PRP = m_graph[ index[referrer] ]->PRP(); transferedPRP = (outLinks != 0 ) ? ( PRP / outLinks ) : PRP; qDebug()<< "Graph::prestigePageRank() - neighbor " << referrer << " has PR = " << PRP << " and outLinks = " << outLinks << " will transfer " << transferedPRP ; sumInLinksPR += transferedPRP; } ++jt; } PRP = (1-d_factor) / aVert + d_factor * sumInLinksPR; (*it) -> setPRP ( PRP ); sumPRP+=PRP; qDebug() << "Graph::prestigePageRank() - Node " << (*it)->name() << " new PR = " << PRP << " old PR was = " << oldPRP << " diff = " << fabs(PRP - oldPRP); // calculate diff from last PageRank value for this vertex // and set it to minDelta if the latter is bigger. if ( maxDelta < fabs(PRP - oldPRP) ) { maxDelta = fabs(PRP - oldPRP); qDebug()<< "Graph::prestigePageRank() - Setting new maxDelta = " << maxDelta; } } // normalize in every iteration qDebug() << "Graph::prestigePageRank() - sumPRP for this iteration " << sumPRP; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { PRP = (*it)->PRP(); if ( PRP > maxPRP ) { maxPRP = PRP; maxNodePRP=(*it)->name(); } if ( PRP < minPRP ) { minPRP = PRP; minNodePRP=(*it)->name(); } } iterations++; } if (aVert != 0 ) meanPRP = sumPRP / aVert ; else meanPRP = SPRP; qDebug() << "sumPRP = " << sumPRP << " aVert = " << aVert << " meanPRP = " << meanPRP; // calculate std and min/max PRPs for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { if (dropIsolates && (*it)->isIsolated()) { continue; } PRP=(*it)->PRP(); resolveClasses(PRP,discretePRPs,classesPRP); SPRP = PRP / maxPRP ; (*it)->setSPRP( SPRP ); qDebug()<< "Graph::prestigePageRank() vertex: " << (*it)->name() << " PR = " << PRP << " standard PR = " << SPRP << " t_sumPRP " << t_sumPRP; t_variance = ( PRP - meanPRP ) ; t_variance *=t_variance; qDebug() << "PRP " << (*it)->PRP() << " t_variance " << PRP - meanPRP << " t_variance^2" << t_variance ; variancePRP += t_variance; } qDebug() << "PRP' Variance " << variancePRP << " aVert " << aVert ; variancePRP = variancePRP / (aVert); qDebug() << "PRP' Variance: " << variancePRP ; calculatedPRP= true; return; } /** * @brief Graph::writePrestigePageRank * Writes the PageRank indices to a file * @param fileName * @param dropIsolates */ void Graph::writePrestigePageRank(const QString fileName, const bool dropIsolates){ QTime computationTimer; computationTimer.start(); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); emit statusMessage ( (tr("Calculating PageRank indices. Please wait...")) ); prestigePageRank(dropIsolates); emit statusMessage ( tr("Writing PageRank indices to file:") + fileName ); outText.setRealNumberPrecision(m_precision); int rowCount=0; int N = vertices(); outText << htmlHead; outText.setRealNumberPrecision(m_precision); outText << "

"; outText << tr("PAGERANK PRESTIGE (PRP)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The PRP is an importance ranking index for each node based on the structure " "of its incoming links/edges and the rank of the nodes linking to it.
" "For each node u the algorithm counts all inbound links (edges) to it, but it " "normalizes each inbound link from a node v by the outDegree of v.
" "The PR values correspond to the principal eigenvector of the normalized link matrix.
" "Note: In weighted relations, each backlink to a node u from another node v is considered " "to have weight=1 but it is normalized by the sum of outbound edge weights of v" "Therefore, nodes with high outLink weights give smaller percentage of their PR to node u." ) << "
" << tr("PRP' is the scaled PRP (PRP divided by max PRP).") << "

"; outText << "

" << "" << tr("PRP range: ") <<"" << tr("(1-d)/N = ") << ( ( 1- d_factor ) / N ) << tr(" ≤ PRP ") << "

"; outText << "

" << "" << tr("PRP' range: ") <<"" << tr("0 ≤ PRP' ≤ 1") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; QList::const_iterator it; for (it= m_graph.cbegin(); it!= m_graph.cend(); ++it){ rowCount++; outText <isIsolated()) { outText << "" <<"" <<""; } else { outText << "" <<"" <<""; } } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("PRP") << "" << tr("PRP'") << "" << tr("%PRP'") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << "--" << "" << "--" << "" << "--" << "
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*it)->PRP() << "" << (*it)->SPRP() << "" << (100* ((*it)->SPRP())) << "
"; if ( minPRP == maxPRP) { outText << "

" << tr("All nodes have the same PRP score.") << "

"; } else { outText << "

"; outText << "" << tr("Max PRP = ") <<"" << maxPRP <<" (node "<< maxNodePRP << ")" << "
" << "" << tr("Min PRP = ") <<"" << minPRP <<" (node "<< minNodePRP << ")" << "
" << "" << tr("PRP classes = ") <<"" << classesPRP << "

"; } outText << "

"; outText << "" << tr("PRP Sum = ") <<"" << sumPRP <<"
" << "" << tr("PRP Mean = ") <<"" << meanPRP <<"
" << "" << tr("PRP Variance = ") <<"" << variancePRP <<"
"; outText << "

"; outText << "

 

"; outText << "

"; outText << tr("PageRank Prestige report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** * @brief Graph::layoutCircularByProminenceIndex * Repositions all nodes on the periphery of different circles with radius * analogous to their prominence index * @param x0 * @param y0 * @param maxRadius * @param prominenceIndex * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::layoutCircularByProminenceIndex(double x0, double y0, double maxRadius, int prominenceIndex, const bool considerWeights, const bool inverseWeights, const bool dropIsolates){ qDebug() << "Graph::layoutCircularByProminenceIndex - " << "prominenceIndex index = " << prominenceIndex; double rad=0; double i=0, std=0; //offset controls how far from the centre the central nodes be positioned float C=0, maxC=0, offset=0.06; double new_radius=0, new_x=0, new_y=0; emit statusMessage(tr("Computing centrality indices. Please wait...")); //first calculate centrality indices if needed maxRadius = canvasMaxRadius(); if ( prominenceIndex == 1) { if (graphModified() || !calculatedDC ) centralityDegree(true, dropIsolates); } else if ( prominenceIndex == 3 ){ if (graphModified() || !calculatedIRCC ) centralityClosenessIR(); } else if ( prominenceIndex == 8 ) { if (graphModified() || !calculatedIC ) centralityInformation(); } else if ( prominenceIndex == 9 ) { if (graphModified() || !calculatedEVC ) centralityEigenvector(); } else if ( prominenceIndex == 10){ if (graphModified() || !calculatedDP ) prestigeDegree(true, dropIsolates); } else if ( prominenceIndex == 11 ) { if (graphModified() || !calculatedPRP ) prestigePageRank(); } else if ( prominenceIndex == 12 ){ if (graphModified() || !calculatedPP ) prestigeProximity(considerWeights, inverseWeights); } else{ if (graphModified() || !calculatedCentralities ) graphMatrixDistancesCreate(true, considerWeights, inverseWeights, dropIsolates); } emit statusMessage(tr("Applying circular layout based on centrality/prestige score. " "Please wait...")); int vert=vertices(); QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ switch (prominenceIndex) { case 1 : { qDebug("Layout according to DC"); C=(*it)->SDC(); std= (*it)->SDC(); maxC=maxSDC; break; } case 2 : { qDebug("Layout according to CC"); C=(*it)->CC(); std= (*it)->SCC(); maxC=maxSCC; break; } case 3 : { qDebug("Layout according to IRCC"); qDebug() << "Layout according to IRCC C = " << (*it)->IRCC() << "std = " << (*it)->SIRCC() << "maxC= " << maxIRCC; C=(*it)->IRCC(); std= (*it)->SIRCC(); maxC=maxIRCC; break; } case 4 : { qDebug("Layout according to BC"); C=(*it)->BC(); std= (*it)->SBC(); maxC=maxSBC; break; } case 5 : { qDebug("Layout according to SC"); C=(*it)->SC(); std= (*it)->SSC(); maxC=maxSSC; break; } case 6 : { qDebug("Layout according to EC"); C=(*it)->EC(); std= (*it)->SEC(); maxC=maxEC; break; } case 7 : { qDebug("Layout according to PC"); C=(*it)->PC(); std= (*it)->SPC(); maxC=maxSPC; break; } case 8 : { qDebug("Layout according to IC"); C=(*it)->IC(); std= (*it)->SIC(); maxC=maxIC; break; } case 9 : { qDebug("Layout according to EVC"); C=(*it)->EVC(); std= (*it)->SEVC(); maxC=1; break; } case 10 : { qDebug("Layout according to DP"); C=(*it)->SDP(); std= (*it)->SDP(); maxC=maxSDP; break; } case 11 : { qDebug("Layout according to PRP"); C=(*it)->PRP(); std= (*it)->SPRP(); maxC=1; break; } case 12 : { qDebug("Layout according to PP"); C=(*it)->PP(); std= (*it)->SPP(); maxC=maxPP; break; } }; qDebug () << "Vertice " << (*it)->name() << " at x=" << (*it)->x() << "y= "<< (*it)->y() << ": C" << C << "stdC" << std << "maxradius" << maxRadius << "newradius" << maxRadius - (std/maxC - offset)*maxRadius; switch (static_cast (ceil(maxC)) ){ case 0: { qDebug("maxC=0. Using maxHeight"); new_radius=maxRadius; break; } default: { new_radius=(maxRadius- (std/maxC - offset)*maxRadius); break; } }; //Calculate new position rad= (2.0* Pi/ vert ); new_x=x0 + new_radius * cos(i * rad); new_y=y0 + new_radius * sin(i * rad); (*it)->setX( new_x ); (*it)->setY( new_y ); qDebug("Finished Calculation. Vertice will move to x=%f and y=%f ", new_x, new_y); //Move node to new position emit setNodePos((*it)->name(), new_x, new_y); i++; emit addGuideCircle ( x0, y0, new_radius ); } graphModifiedSet(GRAPH_CHANGED_POSITIONS); } /** * @brief Graph::layoutRandom Repositions all nodes on different random positions * Emits setNodePos(i, x,y) to tell GW that the node item should be moved. * @param maxWidth * @param maxHeight */ void Graph::layoutRandom(){ qDebug()<< "Graph::layoutRandom() "; double new_x=0, new_y=0; Vertices::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ new_x= canvasRandomX(); new_y= canvasRandomY(); (*it)->setX( new_x ); (*it)->setY( new_y ); qDebug()<< "Graph::layoutRandom() - " << " vertex " << (*it)->name() << " emitting setNodePos to new pos " << new_x << " , "<< new_y; emit setNodePos((*it)->name(), new_x, new_y); } graphModifiedSet(GRAPH_CHANGED_POSITIONS); } /** * @brief Graph::layoutCircularRandom * Repositions all nodes on the periphery of different circles with random radius * @param x0 * @param y0 * @param maxRadius */ void Graph::layoutCircularRandom(double x0, double y0, double maxRadius){ qDebug() << "Graph::layoutCircularRandom - "; double rad=0, new_radius=0, new_x=0, new_y=0; double i=0; maxRadius = canvasMaxRadius(); //offset controls how far from the centre the central nodes be positioned float offset=0.06, randomDecimal=0; int vert=vertices(); QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ randomDecimal = (float ) ( rand()%100 ) / 100.0; new_radius=(maxRadius- (randomDecimal - offset)*maxRadius); qDebug () << "Vertice " << (*it)->name() << " at x=" << (*it)->x() << ", y= "<< (*it)->y() << ", maxradius " << maxRadius << " randomDecimal " << randomDecimal << " new radius " << new_radius; //Calculate new position rad= (2.0* Pi/ vert ); new_x=x0 + new_radius * cos(i * rad); new_y=y0 + new_radius * sin(i * rad); (*it)->setX( new_x ); (*it)->setY( new_y ); qDebug("Vertice will move to x=%f and y=%f ", new_x, new_y); //Move node to new position emit setNodePos((*it)->name(), new_x, new_y); i++; emit addGuideCircle ( x0, y0, new_radius ); } graphModifiedSet(GRAPH_CHANGED_POSITIONS); } /** * @brief Graph::layoutLevelByProminenceIndex * Repositions all nodes on different top-down levels according to their centrality * Emits setNodePos(i, x,y) to tell GW that the node item should be moved. * @param maxWidth * @param maxHeight * @param prominenceIndex * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::layoutLevelByProminenceIndex(double maxWidth, double maxHeight, int prominenceIndex, const bool considerWeights, const bool inverseWeights, const bool dropIsolates){ qDebug("Graph: layoutLevelCentrality..."); double i=0, std=0; //offset controls how far from the top the central nodes will be float C=0, maxC=0, offset=50; double new_x=0, new_y=0; // int vert=vertices(); maxHeight-=offset; maxWidth-=offset; QList::const_iterator it; emit statusMessage(tr("Computing centrality/prestige scores. Please wait...")); if ( prominenceIndex == 1) { if (graphModified() || !calculatedDC ) centralityDegree(true, dropIsolates); } else if ( prominenceIndex == 3 ){ if (graphModified() || !calculatedIRCC ) centralityClosenessIR(); } else if ( prominenceIndex == 8 ) { if (graphModified() || !calculatedIC ) centralityInformation(); } else if ( prominenceIndex == 9){ if (graphModified() || !calculatedEVC ) centralityEigenvector(true, dropIsolates); } else if ( prominenceIndex == 10){ if (graphModified() || !calculatedDP ) prestigeDegree(true, dropIsolates); } else if ( prominenceIndex == 11 ) { if (graphModified() || !calculatedPRP ) prestigePageRank(); } else if ( prominenceIndex == 12 ){ if (graphModified() || !calculatedPP ) prestigeProximity(considerWeights, inverseWeights); } else{ if (graphModified() || !calculatedCentralities ) graphMatrixDistancesCreate(true, considerWeights, inverseWeights, dropIsolates); } emit statusMessage(tr("Applying level layout based on centrality/prestige score. " "Please wait...")); for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ switch (prominenceIndex) { case 1 : { qDebug("Layout according to DC"); C=(*it)->SDC(); std= (*it)->SDC(); maxC=maxSDC; break; } case 2 : { qDebug("Layout according to CC"); C=(*it)->CC(); std= (*it)->SCC(); maxC=maxSCC; break; } case 3 : { qDebug("Layout according to IRCC"); C=(*it)->IRCC(); std= (*it)->SIRCC(); maxC=maxIRCC; break; } case 4 : { qDebug("Layout according to BC"); C=(*it)->BC(); std= (*it)->SBC(); maxC=maxSBC; break; } case 5 : { qDebug("Layout according to SC"); C=(*it)->SC(); std= (*it)->SSC(); maxC=maxSSC; break; } case 6 : { qDebug("Layout according to EC"); C=(*it)->EC(); std= (*it)->SEC(); maxC=maxEC; break; } case 7 : { qDebug("Layout according to PC"); C=(*it)->PC(); std= (*it)->SPC(); maxC=maxSPC; break; } case 8 : { qDebug("Layout according to IC"); C=(*it)->IC(); std= (*it)->SIC(); maxC=maxIC; break; } case 9 : { qDebug("Layout according to EVC"); C=(*it)->EVC(); std= (*it)->SEVC(); maxC=1; break; } case 10 : { qDebug("Layout according to DP"); C=(*it)->SDP(); std= (*it)->SDP(); maxC=maxSDP; break; } case 11 : { qDebug("Layout according to PRP"); C=(*it)->PRP(); std= (*it)->SPRP(); maxC=1; break; } case 12 : { qDebug("Layout according to PP"); C=(*it)->PP(); std= (*it)->SPP(); maxC=maxPP; break; } }; qDebug()<< "Vertice " << (*it)->name() << " at x="<< (*it)->x() << ", y="<< (*it)->y() << ": C=" << C << ", stdC=" << std << ", maxC "<< maxC << ", maxWidth " << maxWidth <<" , maxHeight "< (ceil(maxC)) ){ case 0: { qDebug("maxC=0. Using maxHeight"); new_y=maxHeight; break; } default: { new_y=offset/2.0+maxHeight-(std/maxC)*maxHeight; break; } }; new_x=offset/2.0 + rand() % ( static_cast (maxWidth) ); qDebug ("new_x %f, new_y %f", new_x, new_y); (*it)->setX( new_x ); (*it)->setY( new_y ); qDebug() << "Finished Calculation. " "Vertice will move to x="<< new_x << " and y= " << new_y; //Move node to new position emit setNodePos((*it)->name(), new_x, new_y); i++; emit addGuideHLine(new_y); } graphModifiedSet(GRAPH_CHANGED_POSITIONS); } /** * @brief Graph::layoutVerticesSizeByProminenceIndex * changes the node size to be proportinal to given prominence index * @param prominenceIndex */ void Graph::layoutVerticesSizeByProminenceIndex (int prominenceIndex, const bool considerWeights, const bool inverseWeights, const bool dropIsolates){ qDebug() << "Graph::layoutVerticesSizeByProminenceIndex - " << "prominenceIndex index = " << prominenceIndex; double std=0; float C=0, maxC=0; int new_size=0; emit statusMessage(tr("Computing centrality/prestige scores. Please wait...")); //first calculate centrality indices if needed if ( prominenceIndex == 0) { // do nothing } else if ( prominenceIndex == 1) { if (graphModified() || !calculatedDC ) centralityDegree(true, dropIsolates); } else if ( prominenceIndex == 3 ){ if (graphModified() || !calculatedIRCC ) centralityClosenessIR(); } else if ( prominenceIndex == 8 ) { if (graphModified() || !calculatedIC ) centralityInformation(); } else if ( prominenceIndex == 9){ if (graphModified() || !calculatedEVC ) centralityEigenvector(true, dropIsolates); } else if ( prominenceIndex == 10){ if (graphModified() || !calculatedDP ) prestigeDegree(true, dropIsolates); } else if ( prominenceIndex == 11 ) { if (graphModified() || !calculatedPRP ) prestigePageRank(); } else if ( prominenceIndex == 12 ){ if (graphModified() || !calculatedPP ) prestigeProximity(considerWeights, inverseWeights); } else{ if (graphModified() || !calculatedCentralities ) graphMatrixDistancesCreate(true, considerWeights, inverseWeights, dropIsolates); } emit statusMessage(tr("Applying node sizes based on centrality/prestige score. " "Please wait...")); QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ switch (prominenceIndex) { case 0: { C=0;maxC=0; break; } case 1 : { qDebug("VerticesSize according to DC"); C=(*it)->SDC(); std= (*it)->SDC(); maxC=maxSDC; break; } case 2 : { qDebug("VerticesSize according to CC"); C=(*it)->CC(); std= (*it)->SCC(); maxC=maxSCC; break; } case 3 : { qDebug("VerticesSize according to IRCC"); C=(*it)->IRCC(); std= (*it)->SIRCC(); maxC=maxIRCC; break; } case 4 : { qDebug("VerticesSize according to BC"); C=(*it)->BC(); std= (*it)->SBC(); maxC=maxSBC; break; } case 5 : { qDebug("VerticesSize according to SC"); C=(*it)->SC(); std= (*it)->SSC(); maxC=maxSSC; break; } case 6 : { qDebug("VerticesSize according to EC"); C=(*it)->EC(); std= (*it)->SEC(); maxC=maxEC; break; } case 7 : { qDebug("VerticesSize according to PC"); C=(*it)->PC(); std= (*it)->SPC(); maxC=maxSPC; break; } case 8 : { qDebug("VerticesSize according to IC"); C=(*it)->IC(); std= (*it)->SIC(); maxC=maxIC; break; } case 9 : { qDebug("Layout according to EVC"); C=(*it)->EVC(); std= (*it)->SEVC(); maxC=1; break; } case 10 : { qDebug("VerticesSize according to DP"); C=(*it)->SDP(); std= (*it)->SDP(); maxC=maxSDP; break; } case 11 : { qDebug("VerticesSize according to PRP"); C=(*it)->PRP(); std= (*it)->SPRP(); maxC=1; break; } case 12 : { qDebug("VerticesSize according to PP"); C=(*it)->PP(); std= (*it)->SPP(); maxC=maxPP; break; } }; qDebug () << "Vertex " << (*it)->name() << ": C=" << C << ", stdC=" << std << ", maxC " << maxC << "initVertexSize " << initVertexSize << " stdC/maxC " << std/maxC << ", (std/maxC) * initVertexSize " << (std/maxC *initVertexSize); switch (static_cast (ceil(maxC) )){ case 0: { qDebug()<<"maxC=0. Using initVertexSize"; new_size=initVertexSize; //emit signal to change node size emit setNodeSize((*it)->name(), new_size); break; } default: { //Calculate new size new_size=ceil ( initVertexSize/2.0 + (float) initVertexSize * (std/maxC)); qDebug ()<< "new vertex size "<< new_size << " call setSize()"; (*it)->setSize(new_size); //emit signal to change node size emit setNodeSize((*it)->name(), new_size); break; } }; } graphModifiedSet(GRAPH_CHANGED_POSITIONS); } /** * @brief Graph::randomizeThings * Adds a little universal randomness :) */ void Graph::randomizeThings() { time_t now; /* define 'now'. time_t is probably a typedef */ now = time((time_t *)NULL); /* Get the system time and put it * into 'now' as 'calender time' the number of seconds since 1/1/1970 */ srand( (unsigned int ) now); } /** * @brief Graph::randomNetErdosCreate * @param vert * @param model * @param edges * @param eprob * @param mode * @param diag * Create an erdos-renyi random network according to the given model */ void Graph::randomNetErdosCreate( const int &vert, const QString &model, const int &edges, const float &eprob, const QString &mode, const bool &diag) { qDebug() << "Graph::randomNetErdosCreate() - vertices " << vert << " model " << model << " edges " << edges << " edge probability " << eprob << " graph mode " << mode << " diag " << diag; if (mode=="graph") { graphUndirectedSet(true); } index.reserve(vert); randomizeThings(); int progressCounter=0; int edgeCount = 0; qDebug() << "Graph::randomNetErdosCreate() - Creating nodes..."; for (int i=0; i< vert ; i++) { int x=canvasRandomX(); int y=canvasRandomY(); qDebug("Graph: randomNetErdosCreate, new node i=%i, at x=%i, y=%i", i+1, x,y); vertexCreate ( i+1, initVertexSize, initVertexColor, initVertexNumberColor, initVertexNumberSize, QString::number (i+1), initVertexLabelColor, initVertexLabelSize, QPoint(x, y), initVertexShape, false ); } qDebug() << "Graph::randomNetErdosCreate() - Creating edges..."; if ( model == "G(n,p)") { qDebug() << "Graph::randomNetErdosCreate() - G(n,p) model..."; for (int i=0;i " << j+1; if (!diag && i==j) { qDebug()<< " Graph::randomNetErdosCreate() - skip because " << i+1 << " = " << j+1 << " and diag " << diag; continue; } if ( ( rand() % 100 + 1 ) / 100.0 < eprob ) { edgeCount ++ ; if (mode == "graph") { qDebug() << "Graph::randomNetErdosCreate() - " <<" create undirected Edge no " << edgeCount; edgeCreate(i+1, j+1, 1, initEdgeColor, EDGE_RECIPROCAL_UNDIRECTED, false, false, QString::null, false); } else { qDebug() << "Graph::randomNetErdosCreate() - " <<" create directed Edge no " << edgeCount; edgeCreate(i+1, j+1, 1, initEdgeColor, EDGE_DIRECTED, true, false, QString::null, false); } } else qDebug() << "Graph::randomNetErdosCreate() - do not create Edge"; } emit updateProgressDialog(++progressCounter ); } } else { qDebug() << "Graph::randomNetErdosCreate() - G(n,M) model..."; int source = 0, target = 0 ; do { source = rand() % vert + 1; target = rand() % vert + 1; qDebug() << "Graph::randomNetErdosCreate() - random pair " << " " << source << " , " << target ; if (!diag && source == target ) { qDebug() << "Graph::randomNetErdosCreate() - skip self loop pair "; continue; } if ( edgeExists(source, target) ) { qDebug() << "Graph::randomNetErdosCreate() - skip pair - exists"; continue; } edgeCount ++; if (mode == "graph") { qDebug() << "Graph::randomNetErdosCreate() - create " << " undirected Edge no " << edgeCount; edgeCreate(source, target, 1, initEdgeColor, EDGE_RECIPROCAL_UNDIRECTED, false, false, QString::null, false); } else { qDebug() << "Graph::randomNetErdosCreate() - create " << " directed Edge no " << edgeCount; edgeCreate(source, target, 1, initEdgeColor, EDGE_DIRECTED, true, false, QString::null, false); } emit updateProgressDialog(++progressCounter ); } while ( edgeCount != edges ); } relationCurrentRename(tr("erdos-renyi"), true); graphModifiedSet(GRAPH_CHANGED_VERTICES_AND_EDGES); } /** * @brief Graph::randomNetRingLatticeCreate * Creates a random ring lattice network. * @param vert * @param degree * @param x0 * @param y0 * @param radius * @param updateProgress */ void Graph::randomNetRingLatticeCreate( const int &vert, const int °ree, const bool updateProgress) { qDebug("Graph: createRingLatticeNetwork"); int x=0; int y=0; int progressCounter=0; double x0 = canvasWidth/2.0; double y0 =canvasHeight/2.0; double radius = canvasMaxRadius(); double rad= (2.0* Pi/ vert ); // if (mode=="graph") { graphUndirectedSet(true); // } randomizeThings(); index.reserve(vert); for (int i=0; i< vert ; i++) { x=x0 + radius * cos(i * rad); y=y0 + radius * sin(i * rad); vertexCreate( i+1,initVertexSize,initVertexColor, initVertexNumberColor, initVertexNumberSize, QString::number (i+1), initVertexLabelColor, initVertexLabelSize, QPoint(x, y), initVertexShape, false); qDebug("Graph: createPhysicistLatticeNetwork, new node i=%i, at x=%i, y=%i", i+1, x,y); } int target = 0; for (int i=0;i " << j+1; edgeCreate (i+1, j+1, 1, initEdgeColor, EDGE_RECIPROCAL_UNDIRECTED, false, false, QString::null, false); } emit updateProgressDialog( ++progressCounter ); } qDebug()<< "Graph::randomNetScaleFreeCreate() - @@@@ " << " start network growth to " << n << " nodes with preferential attachment" ; for (int i= m0 ; i < n ; ++i) { x=x0 + radius * cos(i * rad); y=y0 + radius * sin(i * rad); qDebug() << "Graph::randomNetScaleFreeCreate() - ++++" << " adding new node i " << i+1 << " pos " << x << "," << y ; vertexCreate( i+1, initVertexSize,initVertexColor, initVertexNumberColor, initVertexNumberSize, QString::number (i+1), initVertexLabelColor, initVertexLabelSize, QPoint(x, y), initVertexShape,false ); emit updateProgressDialog( ++progressCounter ); // need to multiply by 2, since we have a undirected graph // and edgesEnabled reports edges/2 sumDegrees = 2 * edgesEnabled(); newEdges = 0; qDebug()<< "Graph::randomNetScaleFreeCreate() - repeat until we reach" << m << "new edges for node" < " "Creating pref.att. reciprocal edge " << i+1 << " <-> " << j+1; edgeCreate (i+1, j+1, 1, initEdgeColor, EDGE_RECIPROCAL_UNDIRECTED, false, false, QString::null, false); newEdges ++; } else { qDebug() << "Graph::randomNetScaleFreeCreate() -----> " "Creating pref.att. directed edge " << i+1 << " <-> " << j+1; edgeCreate (i+1, j+1, 1, initEdgeColor, EDGE_DIRECTED_OPPOSITE_EXISTS, true, false, QString::null, false); newEdges ++; } } } if ( newEdges == m ) break; } qDebug()<< "Graph::randomNetScaleFreeCreate() - " << m << "edges reached " "for node" << i+1; } relationCurrentRename(tr("scale-free"),true); qDebug() << "Graph::randomNetScaleFreeCreate() - finished. Calling " "graphModifiedSet(GRAPH_CHANGED_VERTICES_AND_EDGES)"; graphModifiedSet(GRAPH_CHANGED_VERTICES_AND_EDGES); emit signalNodeSizesByInDegree(true); //FIXME } /** * @brief Graph::randomNetSmallWorldCreate * Creates a small world network * @param vert * @param degree * @param beta */ void Graph::randomNetSmallWorldCreate (const int &vert, const int °ree, const double &beta, const QString &mode) { qDebug() << "Graph:randomNetSmallWorldCreate() -. " << "vertices: " << vert << "degree: " << degree << "beta: " << beta << "mode: " << mode << "First creating a ring lattice"; if (mode=="graph") { graphUndirectedSet(true); } randomNetRingLatticeCreate(vert, degree, false); qDebug("******** Graph: REWIRING starts..."); int candidate; int progressCounter=1; for (int i=1;i>>>> REWIRING: Check if "<< i << " is linked to " << j; if ( edgeExists(i, j) ) { qDebug()<<">>>>> REWIRING: They're linked. Do a random REWIRING " "Experiment between "<< i<< " and " << j << " Beta parameter is " << beta; if (rand() % 100 < (beta * 100)) { qDebug(">>>>> REWIRING: We'l break this edge!"); edgeRemove(i, j, true); qDebug()<<">>>>> REWIRING: OK. Let's create a new edge!"; for (;;) { //do until we create a new edge candidate=rand() % (vert+1) ; //pick another vertex. if (candidate == 0 || candidate == i) continue; qDebug()<<">>>>> REWIRING: Candidate: "<< candidate; //Only if differs from i and hasnot edge with it if ( edgeExists(i, candidate) == 0) qDebug("<----> Random New Edge Experiment between %i and %i:", i, candidate); if (rand() % 100 > 0.5) { qDebug("Creating new link!"); edgeCreate(i, candidate, 1, initEdgeColor, EDGE_RECIPROCAL_UNDIRECTED, false, false, QString::null, false); break; } } } else qDebug("Will not break link!"); } } emit updateProgressDialog( ++progressCounter ); } relationCurrentRename(tr("small-world"), true); emit signalNodeSizesByInDegree(true); graphModifiedSet(GRAPH_CHANGED_VERTICES_AND_EDGES); } /** * @brief Graph::randomNetRegularCreate * Creates a random network where nodes have the same degree. * @param vert * @param degree */ void Graph::randomNetRegularCreate(const int &vert, const int °ree, const QString &mode, const bool &diag){ qDebug() << "Graph::randomNetRegularCreate()"; Q_UNUSED(diag); m_undirected = (mode == "graph") ? true: false; float progressCounter=0; float progressFraction =(m_undirected) ? 2/(float) degree : 1/(float) degree; randomizeThings(); index.reserve(vert); int x = 0, y = 0 ; qDebug()<< "Graph::randomNetRegularCreate() - creating vertices"; for (int i=0; i< vert ; i++) { x=canvasRandomX(); y=canvasRandomY(); qDebug() << "Graph::randomNetRegularCreate() - creating new vertex at " << x << "," << y; vertexCreate( i+1, initVertexSize,initVertexColor, initVertexNumberColor, initVertexNumberSize, QString::number (i+1), initVertexLabelColor, initVertexLabelSize, QPoint(x, y), initVertexShape,false ); } int target = 0; int edgeCount = 0; QList m_edges; QStringList firstEdgeVertices, secondEdgeVertices, m_edge; QString firstEdge, secondEdge; qDebug()<< "Graph::randomNetRegularCreate() - Creating initial edges"; if (mode=="graph") { for (int i=0;i (vert-1)) target = target-vert; qDebug()<< "Graph::randomNetRegularCreate() - undirected edge " << i+1 << "<->"<< target+1; m_edges.append(QString::number(i+1)+"->"+QString::number(target+1)); edgeCount ++; } } } else { for (int i=0;i (vert-1)) target = target-vert; qDebug()<< "Graph::randomNetRegularCreate() - directed edge " << i+1 << "->"<< target+1; m_edges.append(QString::number(i+1)+"->"+QString::number(target+1)); edgeCount ++; } } } qDebug()<< "Graph::randomNetRegularCreate() - Edges created:" << edgeCount << "Edge list count:" << m_edges.size() << "Now reordering all edges in pairs..."; //take randomly two edges, of different vertices and combine their source //and target vertices to two different edges for (int i = 1 ; i< m_edges.size(); ++i) { edgeCount = 0; firstEdgeVertices.clear(); secondEdgeVertices.clear(); firstEdgeVertices << ""; firstEdgeVertices << ""; secondEdgeVertices << ""; secondEdgeVertices << ""; while (firstEdgeVertices[0] == firstEdgeVertices[1] || firstEdgeVertices[0] == secondEdgeVertices[0] || firstEdgeVertices[0] == secondEdgeVertices[1] || firstEdgeVertices[1] == secondEdgeVertices[0] || firstEdgeVertices[1] == secondEdgeVertices[1] || secondEdgeVertices[0] == secondEdgeVertices[1] || m_edges.contains( firstEdgeVertices[0] + "->" + secondEdgeVertices[1] ) || m_edges.contains( secondEdgeVertices[0] + "->" + firstEdgeVertices[1] ) || (m_undirected && m_edges.contains( secondEdgeVertices[1] + "->" + firstEdgeVertices[0]) )|| (m_undirected && m_edges.contains( firstEdgeVertices[1] + "->" + secondEdgeVertices[0] ) ) ) { firstEdge = m_edges.at(rand() % m_edges.size()) ; firstEdgeVertices = firstEdge.split("->"); secondEdge = m_edges.at(rand() % m_edges.size()) ; secondEdgeVertices = secondEdge.split("->"); } qDebug()<< "Graph::randomNetRegularCreate() - removing edges:" <" << firstEdgeVertices[1] << "and" << secondEdgeVertices[0] << "->" << secondEdgeVertices[1] << "edge list count:" << m_edges.size(); m_edges.append( firstEdgeVertices[0]+"->"+secondEdgeVertices[1]); m_edges.append(secondEdgeVertices[0]+"->"+firstEdgeVertices[1]); qDebug()<< "Graph::randomNetRegularCreate() - 2 new edges added:" << firstEdgeVertices[0] << "->" << secondEdgeVertices[1] <<"and" << secondEdgeVertices[0]<<"->"<"); qDebug() << "Graph::randomNetRegularCreate() -" << "Drawing undirected Edge no" << edgeCount << ":" << m_edge[0].toInt(0) << "<->" << m_edge[1].toInt(0); edgeCreate(m_edge[0].toInt(0), m_edge[1].toInt(0), 1, initEdgeColor, (m_undirected) ? EDGE_RECIPROCAL_UNDIRECTED : EDGE_DIRECTED, (m_undirected) ? false:true, false, QString::null, false); edgeCount++; progressCounter +=progressFraction; qDebug() << "Graph::randomNetRegularCreate() -" << "progressCounter " << progressCounter << "fmod ( progressCounter, 1.0) = " << fmod ( progressCounter, 1.0); if ( fmod ( progressCounter, 1.0) == 0) { qDebug() << "Graph::randomNetRegularCreate() - emit updateProgressDialog" << (int) progressCounter ; emit updateProgressDialog( (int) progressCounter ); } } relationCurrentRename(tr("d-regular"), true); graphModifiedSet(GRAPH_CHANGED_VERTICES_AND_EDGES); } /** * @brief Graph::walksBetween * Calculates and returns the number of walks of a given length between v1 and v2 * @param v1 * @param v2 * @param length * @return */ int Graph::walksBetween(int v1, int v2, int length) { graphWalksMatrixCreate(length); return XM.item(v1-1,v2-1); } /** * @brief Computes either the "Walks of given length" or the "Total Walks" matrix. * If length>0, it computes the Walks of given length matrix, XM=AM^l * where each element (i,j) denotes the number of walks of length l between vertex i and j. * If length=0, it computes the Total Walks matrix, XSM=Sum{AM^n} where each (i,j) * denotes the total number of walks of any length between vertices i and j. * NOTE: In the latter case, this function is VERY SLOW on large networks (n>50), * since it will calculate all powers of the sociomatrix up to n-1 in order to find out all * possible walks. * @param length * @param updateProgress */ void Graph::graphWalksMatrixCreate(const int &vertices, const int &length, const bool &updateProgress) { bool dropIsolates=false; bool considerWeights=true; bool inverseWeights=false; bool symmetrize=false; int N = vertices; qDebug()<<"Graph::graphWalksMatrixCreate() - Create adjacency matrix AM"; emit statusMessage(tr("Computing adjacency matrix. Please wait...")); graphMatrixAdjacencyCreate(dropIsolates, considerWeights, inverseWeights, symmetrize); if (length>0) { qDebug()<< "Graph::graphWalksMatrixCreate() - " "Calculating sociomatrix power" << length; if (updateProgress) emit updateProgressDialog (1); emit statusMessage(tr("Computing sociomatrix power %1. Please wait...").arg(length)); XM = AM.pow(length, false); emit updateProgressDialog (length); } else { qDebug()<< "Graph::graphWalksMatrixCreate() - " "Calculating all sociomatrix powers up to" << N-1; XM = AM; // XM will be the product matrix // qDebug() << "Graph::graphWalksMatrixCreate() XM=AM="; // XM.printMatrixConsole(); XSM = AM; // XSM is the sum of product matrices // qDebug() << "Graph::graphWalksMatrixCreate() XSM=AM="; // XSM.printMatrixConsole(); for (int i=2; i <= (N-1) ; ++i) { emit statusMessage(tr("Computing all sociomatrix powers up to %1. " "Now computing A^%2. Please wait...").arg(N-1).arg(i)); XM*=AM; // qDebug() << "Graph::graphWalksMatrixCreate() i"<"; if (length>0) { outText << tr("WALKS OF LENGTH %1 MATRIX").arg(length); } else { outText << tr("TOTAL WALKS MATRIX"); } outText << ""; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; if (length>0) { outText << "

" << tr("The Walks of length %1 matrix is a NxN matrix " "where each element (i,j) is the number of walks of " "length %1 between actor i and actor j, " "or 0 if no walk exists.
" "A walk is a sequence of edges and vertices, where each edge's " "endpoints are the two vertices adjacent to it. In a walk, " "vertices and edges may repeat.
" "Warning: Walks count unordered pairs of nodes. ").arg(length) << "

"; } else { outText << "

" << tr("The Total Walks matrix of a social network is a NxN matrix " "where each element (i,j) is the total number of walks of any " "length (less than or equal to %1) between actor i and actor j, " "or 0 if no walk exists.
" "A walk is a sequence of edges and vertices, where each edge's " "endpoints are the two vertices adjacent to it. In a walk, " "vertices and edges may repeat.
" "Warning: Walks count unordered pairs of nodes. ").arg(N-1) << "

"; } emit statusMessage ( tr("Writing Walks matrix to file:") + fn ); qDebug()<<"Graph::writeMatrixWalks() - Writing XM to file"; if (length > 0) { XM.printHTMLTable(outText); } else { XSM.printHTMLTable(outText); } outText << "

 

"; outText << "

"; outText << tr("Walks report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** Calculates and returns non-zero if vertices v1 and v2 are reachable. If v1, v2 are reachable it returns the geodesic distance. This method is actually a reachability test (if it returns non-zero) */ int Graph::reachable(const int &v1, const int &v2) { qDebug()<< "Graph::reachable()"; if (!calculatedDistances || graphModified() ) graphMatrixDistancesCreate(false); return DM.item(v1-1,v2-1); } /** * Returns the influence range of vertex v1, namely the set of nodes who are * reachable by v1 (See Wasserman and Faust, pp.200-201, based on Lin, 1976). * This function is for digraphs only */ QList Graph::vertexinfluenceRange(int v1){ qDebug() << "Graph::vertexinfluenceRange() "; if (!calculatedDistances|| graphModified()) { // Construct a list of influence ranges for each node graphMatrixDistancesCreate(false, false,false,false); } return influenceRanges.values(v1); } /** * @brief Graph::vertexinfluenceDomain * Returns the influence domain of vertex v1, namely the set of nodes who can * reach v1 * This function applies to digraphs only * @param v1 * @return */ QList Graph::vertexinfluenceDomain(int v1){ qDebug() << "Graph::vertexinfluenceDomain() "; if (!calculatedDistances || graphModified()) { // Construct a list of influence domains for each node graphMatrixDistancesCreate(false, false,false,false); } return influenceDomains.values(v1); } /** Writes the reachability matrix X^R of the graph to a file */ void Graph::writeReachabilityMatrixPlainText(const QString &fn, const bool &dropIsolates) { qDebug("Graph::writeReachabilityMatrixPlainText() "); QFile file (fn); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fn ); return; } QTextStream outText(&file); outText << "-Social Network Visualizer "<< VERSION <"; outText << tr("CLUSTERING COEFFICIENT (CLC) REPORT"); outText << ""; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The local Clustering Coefficient, introduced by Watts and Strogatz (1998) " "quantifies how close each node and its neighbors are to being a complete subgraph (clique).") << "
" << tr("For each node u, the local CLC score is the proportion of actual links between " "its neighbors divided by the number of links that could possibly exist between them.
" "The CLC index is used to characterize the transitivity of a network. A value close to one " "indicates that the node is involved in many transitive relations. " "CLC' is the normalized CLC, divided by maximum CLC found in this network.") << "

"; outText << "

" << "" << tr("CLC range: ") <<"" << tr("0 ≤ CLC ≤ 1 ") << "

"; outText << "

" << "" << tr("CLC range: ") <<"" << tr("0 ≤ CLC' ≤ 1 ") << "

"; outText << ""; outText << "" <<"" <<"" <<"" <<"" <<"" <<"" <<"" << "" <<""; QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ rowCount++; outText <" <<"" <<""; } outText << "
" << tr("Node") << "" << tr("Label") << "" << tr("CLC") << "" << tr("CLC'") << "" << tr("%CLC'") <<"
" << (*it)->name() << "" << ( (! ( (*it)->label().simplified()).isEmpty()) ? (*it)->label().simplified().left(10) : "-" ) << "" << (*it)->CLC() << "" << (*it)->CLC() / maxCLC << "" << 100 * (*it)->CLC() / maxCLC << "
"; if ( minCLC == maxCLC) { outText << "

" << tr("All nodes have the same local CLC score.") << "

"; } else { outText << "

"; outText << "" << tr("Max CLC = ") <<"" << maxCLC <<" (node "<< maxNodeCLC << ")" << "
" << "" << tr("Min CLC = ") <<"" << minCLC <<" (node "<< minNodeCLC << ")" << "
" << "

"; } outText << "

" << "" << tr("CLC Mean = ") <<"" << averageCLC <<"
" << "" << tr("CLC Variance = ") <<"" << varianceCLC <<"
"; outText << "

"; outText << "

"; outText << tr("GROUP / NETWORK AVERAGE CLUSTERING COEFFICIENT (GCLC)") << "

"; outText << "

" << "" << tr("GCLC = ") <<"" << averageCLC << "

"; outText << "

" << tr("Range: 0 < GCLC < 1
") << tr("GCLC = 0, when there are no cliques (i.e. acyclic tree).
") << tr("GCLC = 1, when every node and its neighborhood are complete cliques.") << "

"; outText << "

 

"; outText << "

"; outText << tr("Clustering Coefficient report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } //Writes the triad census to a file void Graph::writeTriadCensus( const QString fileName, const bool considerWeights) { QTime computationTimer; computationTimer.start(); Q_UNUSED(considerWeights); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); emit statusMessage ( (tr("Computing triad census. Please wait....")) ); if (graphModified() || !calculatedTriad) { if (!graphTriadCensus()){ qDebug() << "Error in graphTriadCensus(). Exiting..."; file.close(); return; } } emit statusMessage ( tr("Writing triad census to file: ") + fileName ); int rowCount = 0; int N = vertices(); outText << htmlHead; outText << "

"; outText << tr("TRIAD CENSUS (TRC) REPORT"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("A Triad Census counts all the different types (classes) of observed triads within a network.
" "The triad types are coded and labeled according to their number of mutual, asymmetric and non-existent (null) dyads.
" "SocNetV follows the M-A-N labeling scheme, as described by Holland, Leinhardt and Davis in their studies.
" "In the M-A-N scheme, each triad type has a label with four characters:
") << tr("- The first character is the number of mutual (M) duads in the triad. Possible values: 0, 1, 2, 3.
" "- The second character is the number of asymmetric (A) duads in the triad. Possible values: 0, 1, 2, 3.
" "- The third character is the number of null (N) duads in the triad. Possible values: 0, 1, 2, 3.
" "- The fourth character is infered from features or the nature of the triad, i.e. presence of cycle or transitivity. " "Possible values: none, D (\"Down\"), U (\"Up\"), C (\"Cyclic\"), T (\"Transitive\")") << "

"; outText << ""; outText << "" <<"" <<"" <<"" << "" <<""; QList triadTypes; triadTypes << "003" ; triadTypes << "012" ; triadTypes << "102" ; triadTypes << "021D"; triadTypes << "021U"; triadTypes << "021C"; triadTypes << "111D"; triadTypes << "111U"; triadTypes << "030T"; triadTypes << "030C"; triadTypes << "201" ; triadTypes << "120D"; triadTypes << "120U"; triadTypes << "120C"; triadTypes << "210" ; triadTypes << "300" ; for (int i = 0 ; i<=15 ; i++) { rowCount = i + 1; outText << "" <<"" <<""; } outText << "
" << tr("Type") << "" << tr("Census") // << "" // << tr("Expected Value") <<"
" << triadTypes[i] << "" << triadTypeFreqs[i] << "
"; outText << "

 

"; outText << "

"; outText << tr("Triad Census report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** * @brief Graph::writeCliqueCensus * Writes the number of cliques (maximal connected subgraphs) of each vertex into a given file. * @param fileName * @param considerWeights */ void Graph::writeCliqueCensus( const QString fileName, const bool considerWeights) { QTime computationTimer; computationTimer.start(); qDebug()<< "Graph::writeCliqueCensus() "; Q_UNUSED(considerWeights); bool dendrogram = true; QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } long int N = vertices(); int cliqueCounter=0; int rowCounter = 0; int cliqueSize = 0; int actor2 = 0, actor1=0, index1=0, index2=0; float numerator = 0; QString listString; QList::const_iterator it, it2; emit statusMessage ( tr("Computing clique census. Please wait..") ); graphCliques(); emit statusMessage ( tr("Writing clique census to file: ") + fileName ); QTextStream outText ( &file ); outText.setCodec("UTF-8"); outText << htmlHead; outText.setRealNumberPrecision(m_precision); outText << "

"; outText << tr("CLIQUE CENSUS (CLQs) REPORT"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("A clique is the largest subgroup of actors in the social network who are all " "directly connected to each other (maximal complete subgraph).
" "SocNetV applies the Bron–Kerbosch algorithm to produce a census of all maximal cliques " "in the network and reports some useful statistics such as disaggregation by vertex " "and co-membership information.
") << "

"; outText << "

" << "" << tr("Maximal Cliques found: ") <<"" << m_cliques.count() << "

"; outText << ""; outText << "" <<"" <<"" <<"" << "" <<""; foreach (QList clique, m_cliques) { ++cliqueCounter; outText << ""; listString.truncate(0); while (!clique.empty()) { listString += QString::number (clique.takeFirst()); if (!clique.empty()) listString += " "; } outText <<"" <<""; } outText << "
" << tr("Clique No") << "" << tr("Clique members") << "
" << cliqueCounter << "" << listString << "
"; outText << "

" << "" << tr("Actor by clique analysis: ") <<"" << tr("Proportion of clique members adjacent") << "

"; outText << ""; outText << "" <<"" <<""; for (int listIndex=0; listIndex" << listIndex+1 << ""; } outText <<"" << "" <<""; rowCounter = 0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ rowCounter++; actor1 = (*it)->name(); outText << "" <<""; foreach (QList clique, m_cliques) { numerator = 0; if (clique.contains( actor1 )){ outText <<""; } else { cliqueSize = clique.size(); while (!clique.empty()) { actor2 = clique.takeFirst(); if ( edgeExists( actor1, actor2) ) { numerator++; } } outText <<""; } } outText <<""; } outText << "
" << tr("Actor/Clique") << "
" << actor1 <<"" << "1.000" <<"" << fixed << (numerator/(float) cliqueSize) <<"
"; emit updateProgressDialog(N / 5); outText << "

" << "" << tr("Actor by actor analysis: ") <<"" << tr(" Co-membership matrix") << "

"; outText << ""; outText << "" <<"" <<""; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ actor1 = (*it)->name(); outText << ""; } outText <<"" << "" <<""; rowCounter=0; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ actor1 = (*it)->name(); index1 = index[actor1]; rowCounter++; outText << "" <<""; for (it2=m_graph.cbegin(); it2!=m_graph.cend(); ++it2){ actor2 = (*it2)->name(); index2 = index[actor2]; outText <<""; } outText <<""; } outText << "
" << tr("Actor/Actor") << "" << actor1 << "
" << actor1 <<"" << qSetRealNumberPrecision(0)<< CLQM.item(index1, index2) <<"
"; emit updateProgressDialog(2 * N / 5); outText << "

" << "" << tr("Hierarchical clustering of overlap matrix: ") <<"" << tr("Actors") << "

"; graphClusteringHierarchical(CLQM, graphMetricStrToType("Euclidean"), CLUSTERING_COMPLETE_LINKAGE, false, true, true, false, true); writeClusteringHierarchicalResultsToStream(outText, N, dendrogram); emit updateProgressDialog(3 * N / 5); outText << "

" << "" << tr("Clique by clique analysis: ") <<"" << tr("Co-membership matrix") << "

"; emit updateProgressDialog(4 * N / 5); outText << "

" << "" << tr("Hierarchical clustering of overlap matrix: ") <<"" << tr("Clique") << "

"; emit updateProgressDialog(N); outText << "

 

"; outText << "

"; outText << tr("Clique Census Report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** * @brief Called from Graph::graphCliques to add a new clique (list of vertices) * Adds clique info to each clique member and updates CLQM matrix. * @param list * @return */ void Graph:: graphCliqueAdd(const QList &clique){ m_cliques.insertMulti(clique.count(), clique); qDebug() << "Graph::graphCliqueAdd() - added clique:" << clique << "of size" << clique.count() << "total cliques:" << m_cliques.count(); int index1=0, index2=0, cliqueCount=0; foreach (int actor1, clique) { index1 = index[actor1]; qDebug() << "Graph::graphCliqueAdd() - updating cliques in actor1:" << actor1 << "index:" << index1; m_graph[ index1 ]->cliqueAdd(clique); foreach (int actor2, clique) { index2 = index[actor2]; cliqueCount = CLQM.item(index1, index2); CLQM.setItem( index1, index2, ( cliqueCount + 1) ); qDebug() << "Graph::graphCliqueAdd() - upd. co-membership matrix CLQM" << "actor1:" << actor1 << "actor2:" << actor2 <<"old matrix element: (" << index1<<","< R, QSet P, QSet X) { qDebug () << "Graph::graphCliques() - check if we are at initialization step"; if (R.isEmpty() && P.isEmpty() && X.isEmpty()){ qDebug() << "Graph::graphCliques() - initialization step. R, X empty and P=V(G)"; int V = vertices() ; P.reserve( V ); R.reserve( V ); X.reserve( V ); P=verticesSet(); CLQM.zeroMatrix(V,V); m_cliques.clear(); QList::const_iterator it; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { (*it)->clearCliques(); } } qDebug() << "Graph::graphCliques() - check if P and X are both empty"; if (P.isEmpty() && X.isEmpty()) { qDebug() << "Graph::graphCliques() - P and X are both empty. MAXIMAL clique R=" << R; QList clique = R.toList(); graphCliqueAdd(clique); } int v; QSet N; QSet temp, temp1, temp2; QSet::iterator i = P.begin(); while( i != P.end()) { v = *i; qDebug() << "Graph::graphCliques() - v:" << v << " P:" << P << " P.count=" < addv; addv.insert(v); // dummy set with just v temp = R+addv; temp1 = P&N; temp2 = X&N; qDebug() << "Graph::graphCliques() - v:" << v << "Recursive call to graphCliques ( R ⋃ {v}, P ⋂ N(v), X ⋂ N(v) )" << endl << "N(v):" << N << endl << "R ⋃ {v}:" << temp << endl << "P ⋂ N(v):" << temp1 << endl << "X ⋂ N(v):" << temp2; // find all clique extensions of R that contain v graphCliques( R+addv, P&N, X&N ); qDebug() << "Graph::graphCliques() - v:" << v << "Returned from recursive call. Moving v:"<< v <<" from P to X to be excluded in the future."; // P = P \ v i=P.erase(i); //P-=v; // X = X + v X.insert(v); qDebug() << "Graph::graphCliques() - v:" << v << "FINISHED" << " P=" << P << " P.count:" < clique, m_cliques) { if ( size!=0 ) { if ( clique.size() != size) continue; } if (clique.contains( actor )){ cliqueCounter++; } } return cliqueCounter; } /** * @brief Graph::graphCliquesOfSize * Returns the number of maximal cliques of a given size * @param size * @return */ int Graph::graphCliquesOfSize(const int &size){ qDebug() << "Graph::graphCliquesOfSize()"; return m_cliques.values(size).count(); } /** * @brief Writes Hierarchical Clustering Analysis to a given file * @param fileName * @param matrix * @param similarityMeasure * @param method * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::writeClusteringHierarchical(const QString &fileName, const QString &matrix, const QString &metric, const QString &method, const bool &diagonal, const bool &dendrogram, const bool &considerWeights, const bool &inverseWeights, const bool &dropIsolates) { QTime computationTimer; computationTimer.start(); qDebug()<< "Graph::writeClusteringHierarchical() - matrix:" << matrix << "metric" << metric << "method" << method << "considerWeights:"<"; outText << tr("HIERARCHICAL CLUSTERING (HCA)"); outText << ""; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << "" << tr("Input matrix: ") << "" << matrix << "

"; outText << "

" << "" << tr("Distance/dissimilarity metric: ") <<"" << metric << "

"; outText << "

" << "" << tr("Clustering method/criterion: ") <<"" << method << "

"; outText << "

 

"; outText << "

" << "" << tr("Analysis results") <<"" << "

"; outText << "

" << "" << tr("Structural Equivalence Matrix: ") <<"" << "

"; STR_EQUIV.printHTMLTable(outText,true,false); outText << "

" << "" << tr("Hierarchical Clustering of Equivalence Matrix: ") <<"" << "

"; writeClusteringHierarchicalResultsToStream(outText, N, dendrogram); outText << "

 

"; outText << "

"; outText << tr("Hierarchical Cluster Analysis report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); qDebug()<< "Graph::writeClusteringHierarchical() - finished"; } void Graph::writeClusteringHierarchicalResultsToStream(QTextStream& outText, const int N, const bool &dendrogram) { qDebug()<<"Graph::writeClusteringHierarchicalResultsToStream()"; QMap::const_iterator it; float level; outText << "
";
    outText <<"Seq" << "\t"<<"Level" << "\t"<< "Actors" <";

    if (dendrogram) {

        qDebug()<<"SVG";

        outText << "

" << "" << tr("Clustering Dendrogram (SVG)") <<"" << "

"; int diagramMaxWidth = 1000; int diagramPaddingLeft=30; int diagramPaddingTop =30; int rowHeight = 15; int rowPaddingLeft = 5; int headerHeight = 10; int headerTextSize = 9; int actorTextSize = 12; int legendTextSize = 9; int maxSVGWidth = diagramMaxWidth + diagramPaddingLeft + rowPaddingLeft; int maxSVGHeight = 2 * diagramPaddingTop + (rowHeight * N); QMap clusterEndPoint; QPoint endPoint1, endPoint2, endPointLevel; QMap::const_iterator pit; //cluster names pair iterator QVector clusterVector; int actorNumber; float maxLevelValue; QString clusterName; QList legendLevelsDone; it = m_clustersPerSequence.constEnd(); it--; maxLevelValue = m_clusteringLevel.last() ; clusterVector.reserve(N); qDebug() << "DENDRO SVG" << "m_clustersPerSequence"<"; outText << ""; // print a legend on top outText << "" << "Actor" <<""; outText << "" << "Clusterings" <<""; // print actor numbers // and compute initial cluster end points for them. for ( int i=0; i < it.value().size() ; ++i ) { actorNumber = it.value().at(i); clusterEndPoint[QString::number(actorNumber)] = QPoint(diagramPaddingLeft,diagramPaddingTop+rowHeight*(i)); outText << ""; outText << "" << actorNumber <<""; outText << ""; // end actor name } // end for rows // begin drawing clustering paths/lines for ( pit= m_clusterPairNamesPerSeq.constBegin() ; pit != m_clusterPairNamesPerSeq.constEnd(); ++pit) { level = m_clusteringLevel.at ( pit.key() - 1); qDebug() << "seq" <"; //stroke-dasharray=\"5,5\" // print level vertical dashed line outText << ""; //print legend if (!legendLevelsDone.contains(level)) { outText << "" << fixed << level <<""; legendLevelsDone.append(level); } } outText << ""; //end dendrogram svg outText << ""; //end dendrogram div } // end if dendrogram } /** * @brief Performs an hierarchical clustering process (Johnson, 1967) on a given * NxN distance/dissimilarity matrix. The input matrix can be the * the adjacency matrix, the geodesic distance matrix or a derived from them * dissimilarities matrix using a user-specified metric, i.e. euclidean distance. * The method parameter defines how to compute distances (similarities) between * a new cluster the old clusters. Valid values can be: * - CLUSTERING_SINGLE_LINKAGE: "single-link" or "connectedness" or "minimum" * - CLUSTERING_COMPLETE_LINKAGE: "complete-link" or "diameter" or "maximum" * - CLUSTERING_AVERAGE_LINKAGE: "average-link" or UPGMA * @param matrix * @param metric * @param method * @param considerWeights * @param inverseWeights * @param dropIsolates */ void Graph::graphClusteringHierarchical(Matrix &STR_EQUIV, const int &metric, const int &method, const bool &diagonal, const bool &diagram, const bool &considerWeights, const bool &inverseWeights, const bool &dropIsolates) { Q_UNUSED (inverseWeights); Q_UNUSED (dropIsolates); qDebug() << "Graph::graphClusteringHierarchical() - " << "metric" << metric << "method" << graphClusteringMethodTypeToString(method) << "diagonal" << diagonal << "diagram" << diagram; STR_EQUIV.printMatrixConsole(true); QString varLocation = "Rows"; float min=RAND_MAX; float max=0; int imin, jmin, imax, jmax, mergedClusterIndex, deletedClusterIndex ; float distanceNewCluster; // temp vector stores cluster members at each clustering level QVector clusteredItems; // maps original and clustered items per their DSM matrix index // so that we know that at Level X the matrix index 0 corresponds to the cluster i.e. { 1,2,4} QMap m_clustersIndex; QMap::iterator it; QMap::iterator prev; QMap::const_iterator sit; // variables for diagram computation QVector clusterPairNames; QString cluster1, cluster2; Matrix DSM; //dissimilarities matrix. Note: will be destroyed in the end. // TODO: needs fix when distances matrix with -1 (infinity) elements is used. // compute, if needed, the dissimilarities matrix switch (metric) { case METRIC_NONE: DSM=STR_EQUIV; break; case METRIC_JACCARD_INDEX: graphMatrixDissimilaritiesCreate(STR_EQUIV, DSM, metric,varLocation,diagonal, considerWeights); STR_EQUIV = DSM; break; case METRIC_MANHATTAN_DISTANCE: graphMatrixDissimilaritiesCreate(STR_EQUIV, DSM, metric,varLocation,diagonal, considerWeights); STR_EQUIV = DSM; break; case METRIC_HAMMING_DISTANCE: graphMatrixDissimilaritiesCreate(STR_EQUIV, DSM, metric,varLocation,diagonal, considerWeights); STR_EQUIV = DSM; break; case METRIC_EUCLIDEAN_DISTANCE: graphMatrixDissimilaritiesCreate(STR_EQUIV, DSM, metric,varLocation,diagonal, considerWeights); STR_EQUIV = DSM; break; case METRIC_CHEBYSHEV_MAXIMUM: graphMatrixDissimilaritiesCreate(STR_EQUIV, DSM, metric,varLocation,diagonal, considerWeights); STR_EQUIV = DSM; break; default: break; } int N = DSM.rows(); qDebug() << "Graph::graphClusteringHierarchical() -" << "initial matrix DSM.size:" << N <<"matrix DSM contents"; DSM.printMatrixConsole(); clusteredItems.reserve(N); if (diagram) { clusterPairNames.reserve(N); } m_clustersIndex.clear(); m_clustersPerSequence.clear(); m_clusteringLevel.clear(); m_clustersByName.clear(); m_clusterPairNamesPerSeq.clear(); // //Step 1: Assign each of the N items to its own cluster. // We have N unit clusters // int clustersLeft = N; int seq = 1 ; //clustering stage/level sequence number for (int i = 0 ; i< N ; i ++ ) { clusteredItems.clear(); clusteredItems << i+1; m_clustersIndex[i] = clusteredItems; if (diagram) { m_clustersByName.insert(QString::number(i+1),clusteredItems ); } } while (clustersLeft > 1) { qDebug() << "Graph::graphClusteringHierarchical() -" <<"matrix DSM contents"; DSM.printMatrixConsole(); // //Step 2. Find the most similar pair of clusters. // Merge them into a single new cluster. // DSM.NeighboursNearestFarthest(min, max, imin, jmin, imax, jmax); mergedClusterIndex = (imin < jmin ) ? imin : jmin; deletedClusterIndex = (mergedClusterIndex == imin ) ? jmin : imin; m_clusteringLevel << min; clusteredItems.clear(); clusteredItems = m_clustersIndex[mergedClusterIndex] + m_clustersIndex[deletedClusterIndex] ; qDebug() << "level"<< min << "seq" << seq <<"clusteredItems in level" < DSM.item(i,jmin) ) ? DSM.item(i,imin) : DSM.item(i,jmin); } qDebug() << "Graph::graphClusteringHierarchical() - " << " DSM("< 0, when two actors have some differences in their ties/distances, \n" "i.e. SMMC = 3 means the two actors have 3 differences in their tie/distance profiles to other actors."); } else { outText << tr("SMMC = 0, when there is no tie profile similarity at all.")< 0, when two actors have some matches in their ties/distances, \n" "i.e. SMMC = 1 means the two actors have their ties to other actors exactly the same all the time."); } outText << endl<< endl; outText << tr("Similarity Matrix by Matching Measure Report,\n"); outText << tr("Created by SocNetV ") << VERSION << ": " << actualDateTime.currentDateTime() .toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) << "\n\n"; file.close(); } /** * @brief Writes dissimilarity matrix based on a metric/measure to given html file * @param fileName * @param measure * @param varLocation * @param diagonal * @param considerWeights */ void Graph::writeMatrixDissimilarities(const QString fileName, const QString &metricStr, const QString &varLocation, const bool &diagonal, const bool &considerWeights) { qDebug()<< "Graph::writeMatrixDissimilarities()" << "metric" << metricStr << "varLocation" << varLocation << "diagonal"<"; outText << tr("DISSIMILARITIES MATRIX"); outText << ""; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << "" << tr("Variables in: ") <<"" << ((varLocation != "Rows" && varLocation != "Columns") ? "Concatenated rows + columns " : varLocation) << "

"; outText << "

" << "" << tr("Metric: ") << "" << metricStr << "

"; outText << "

" << "" << tr("Diagonal: ") <<"" << ((diagonal) ? "Included" : "Not included") << "

"; outText << "

" << "" << tr("Range: ") <<""; if (metric==METRIC_JACCARD_INDEX) outText << tr("0 < C < 1") ; else outText << tr("0 < C ") ; outText << "

"; outText << "

" << "
" << "" << tr("Analysis results ") <<"" << "

"; DSM.printHTMLTable(outText); outText << "

"; outText << "" << tr("DSM = 0 ") <<"" << tr("when two actors have no tie profile dissimilarities. The actors have the same ties to all others.") <<"
" << "" << tr("DSM > 0 ") <<"" << tr("when the two actors have differences in their ties to other actors."); outText << "

"; outText << "

 

"; outText << "

"; outText << tr("Dissimilarity Matrix Report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** * @brief Calls Matrix:distancesMatrix to compute the dissimilarities matrix DSM * of the variables (rows, columns, both) in given input matrix using the * user defined metric * @param AM * @param DSM * @param metric * @param varLocation * @param diagonal * @param considerWeights */ void Graph::graphMatrixDissimilaritiesCreate(Matrix &INPUT_MATRIX, Matrix &DSM, const int &metric, const QString &varLocation, const bool &diagonal, const bool &considerWeights){ qDebug()<<"Graph::graphMatrixDissimilaritiesCreate()"; DSM = INPUT_MATRIX.distancesMatrix(metric, varLocation, diagonal, considerWeights); qDebug()<<"Graph::graphMatrixDissimilaritiesCreate() - matrix SCM"; } /** * @brief Writes similarity matrix based on a matching measure to given html file * @param fileName * @param measure * @param matrix * @param varLocation * @param diagonal * @param considerWeights */ void Graph::writeMatrixSimilarityMatching(const QString fileName, const QString &measure, const QString &matrix, const QString &varLocation, const bool &diagonal, const bool &considerWeights) { QTime computationTimer; computationTimer.start(); int measureInt = graphMetricStrToType( measure ); Q_UNUSED(considerWeights); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); emit statusMessage ( (tr("Examining pair-wise similarity of actors...")) ); Matrix SCM; int N = vertices(); if (matrix == "Adjacency") { graphMatrixAdjacencyCreate(); graphMatrixSimilarityMatchingCreate(AM, SCM, measureInt , varLocation, diagonal, considerWeights); } else if (matrix == "Distances") { graphMatrixDistancesCreate(); graphMatrixSimilarityMatchingCreate(DM, SCM, measureInt, varLocation, diagonal, considerWeights); } else { return; } emit statusMessage ( tr("Writing similarity coefficients to file: ") + fileName ); outText.setRealNumberPrecision(m_precision); outText << htmlHead; outText << "

"; outText << tr("SIMILARITY MATRIX: MATCHING COEFFICIENTS (SMMC)"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << "" << tr("Input matrix: ") <<"" << matrix << "

"; outText << "

" << "" << tr("Variables in: ") <<"" << ((varLocation != "Rows" && varLocation != "Columns") ? "Concatenated rows + columns " : varLocation) << "

"; outText << "

" << "" << tr("Matching measure: ") << "" << measure << "

"; outText << "

" << "" << tr("Diagonal: ") <<"" << ((diagonal) ? "Included" : "Not included") << "

"; outText << "

" << "" << tr("SMMC range: ") <<""; if (measureInt==METRIC_HAMMING_DISTANCE) outText << tr("0 < C") ; else outText << tr("0 < C < 1") ; outText << "

"; outText << "

" << "
" << "" << tr("Analysis results ") <<"" << "

"; SCM.printHTMLTable(outText); outText << "

"; if (measureInt==METRIC_HAMMING_DISTANCE) { outText << "" << tr("SMMC = 0 ") <<"" << tr("when two actors are absolutely similar (no tie/distance differences).") <<"
" << "" << tr("SMMC > 0 ") <<"" << tr("when two actors have some differences in their ties/distances, " "i.e. SMMC = 3 means the two actors have 3 differences in their tie/distance profiles to other actors."); } else { outText << "" << tr("SMMC = 0 ") <<"" << tr("when there is no tie profile similarity at all.") <<"
" << "" << tr("SMMC > 0 ") <<"" << tr("when two actors have some matches in their ties/distances, " "i.e. SMMC = 1 means the two actors have their ties to other actors exactly the same all the time."); } outText << "

"; outText << "

 

"; outText << "

"; outText << tr("Similarity Matrix by Matching Measure Report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** * @brief Calls Matrix:similarityMatrix to compute the similarity matrix SCM * of the variables (rows, columns, both) in given input matrix using the * selected matching measure. * * @param AM * @param SCM * @param rows */ void Graph::graphMatrixSimilarityMatchingCreate (Matrix &AM, Matrix &SCM, const int &measure, const QString &varLocation, const bool &diagonal, const bool &considerWeights){ qDebug()<<"Graph::graphMatrixSimilarityMatchingCreate()"; SCM.similarityMatrix(AM, measure, varLocation, diagonal, considerWeights); qDebug()<<"Graph::graphMatrixSimilarityMatchingCreate() - matrix SCM"; //SCM.printMatrixConsole(true); } /** * @brief Calls Graph::graphMatrixSimilarityPearsonCreate() and * writes Pearson Correlation Coefficients to given file * @param fileName * @param considerWeights */ void Graph::writeMatrixSimilarityPearson(const QString fileName, const bool considerWeights, const QString &matrix, const QString &varLocation, const bool &diagonal) { QTime computationTimer; computationTimer.start(); Q_UNUSED(considerWeights); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); emit statusMessage ( (tr("Calculating Pearson Correlations...")) ); Matrix PCC; int N = vertices(); if (matrix == "Adjacency") { graphMatrixAdjacencyCreate(); graphMatrixSimilarityPearsonCreate(AM, PCC, varLocation,diagonal); } else if (matrix == "Distances") { graphMatrixDistancesCreate(); graphMatrixSimilarityPearsonCreate(DM, PCC, varLocation,diagonal); } else { return; } emit statusMessage ( tr("Writing Pearson coefficients to file: ") + fileName ); outText.setRealNumberPrecision(m_precision); outText << htmlHead; outText << "

"; outText << tr("PEARSON CORRELATION COEFFICIENTS (PCC) MATRIX"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << "" << tr("Input matrix: ") <<"" << matrix << "

"; outText << "

" << "" << tr("Variables in: ") <<"" << ((varLocation != "Rows" && varLocation != "Columns") ? "Concatenated rows + columns " : varLocation) << "

"; outText << "

" << "" << tr("Diagonal: ") <<"" << ((diagonal) ? "Included" : "Not included") << "

"; outText << "

" << "" << tr("PCC range: ") <<"" << "-1 < C < 1" << "

"; outText << "

" << "" << "
" << tr("Analysis results ") <<"
" << "

"; PCC.printHTMLTable(outText); outText << "

"; outText << "" << tr("PCC = 0 ") <<"" << tr("when there is no correlation at all.") <<"
" << "" << tr("PCC > 0 ") <<"" << tr("when there is positive correlation, " "i.e. +1 means actors with same patterns of ties/distances.") <<"
" << "" << tr("PCC < 0 ") <<"" << tr("when there is negative correlation, " "i.e. -1 for actors with exactly opposite patterns of ties.") <<"
"; outText << "

"; outText << "

 

"; outText << "

"; outText << tr("Pearson Correlation Coefficients Report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** * @brief * Calls Graph::graphSimilariyPearsonCorrelationCoefficients() and * writes Pearson Correlation Coefficients to given file * @param fileName * @param considerWeights */ void Graph::writeMatrixSimilarityPearsonPlainText(const QString fileName, const bool considerWeights, const QString &matrix, const QString &varLocation, const bool &diagonal) { Q_UNUSED(considerWeights); QFile file ( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText ( &file ); outText.setCodec("UTF-8"); emit statusMessage ( (tr("Calculating Pearson Correlations...")) ); Matrix PCC; if (matrix == "Adjacency") { graphMatrixAdjacencyCreate(); graphMatrixSimilarityPearsonCreate(AM, PCC, varLocation,diagonal); } else if (matrix == "Distances") { graphMatrixDistancesCreate(); graphMatrixSimilarityPearsonCreate(DM, PCC, varLocation,diagonal); } else { return; } emit statusMessage ( tr("Writing Pearson coefficients to file: ") + fileName ); outText.setRealNumberPrecision(m_precision); outText << tr("PEARSON CORRELATION COEFFICIENTS (PCC) MATRIX") << endl< 0, when there is positive correlation, i.e. +1 means actors with same patterns of ties/distances.\n"); outText << tr( "PCC < 0, when there is negative correlation, i.e. -1 for actors with exactly opposite patterns of ties.\n"); outText <<"\n\n" ; outText << tr("Pearson Correlation Coefficients Report,\n"); outText << tr("Created by SocNetV ") << VERSION << ": " << actualDateTime.currentDateTime() .toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) << "\n\n"; file.close(); } /** * @brief * The Pearson product-moment correlation coefficient (PPMCC, PCC or Pearson's r) * is a measure of the linear dependence between two variables X and Y. * * As a normalized version of the covariance, the PPMCC is computed with the formula: * r =\frac{\sum ^n _{i=1}(x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum ^n _{i=1}(x_i - \bar{x})^2} \sqrt{\sum ^n _{i=1}(y_i - \bar{y})^2}} * * It gives a value between +1 and −1 inclusive, where 1 is total positive linear * correlation, 0 is no linear correlation, and −1 is total negative linear correlation. * * In SNA, Pearson correlations can be used to track the similarity between actors, * in terms of structural equivalence. * * This method creates an actor by actor NxN matrix PCC where the (i,j) element * is the Pearson correlation coefficient of actor i and actor j. * If the input matrix is the adjacency matrix, the PCC of two nodes measures * how related (similar, inverse or not related at all) their patterns of ties tend to be. * A positive value means there is strong linear association of the two actors, * while a negative value means the inverse. For instance a value of -1 means * the two actors have exactly opposite ties to other actors, while a value of 1 * means the actors have identical patterns of ties to other actors * (they are connected to the same actors). * * The correlation measure of similarity is particularly useful when the data on ties are valued * @param AM * @param PCC * @param rows */ void Graph::graphMatrixSimilarityPearsonCreate (Matrix &AM, Matrix &PCC, const QString &varLocation, const bool &diagonal){ qDebug()<<"Graph::graphMatrixSimilarityPearsonCreate()"; PCC.pearsonCorrelationCoefficients(AM, varLocation,diagonal); qDebug()<<"Graph::graphMatrixSimilarityPearsonCreate() - matrix PCC"; //PCC.printMatrixConsole(true); } /** Returns the number of triples of vertex v1 A triple Υ at a vertex v is a path of length two for which v is the center vertex. */ float Graph::numberOfTriples(int v1){ float totalDegree=0; if (graphSymmetric()){ totalDegree=vertexEdgesOutbound(v1); return totalDegree * (totalDegree -1.0) / 2.0; } totalDegree=vertexEdgesOutbound(v1) + vertexEdgesInbound(v1); //FIXEM return totalDegree * (totalDegree -1.0); } /** * @brief Graph::clusteringCoefficientLocal * Returns the local clustering coefficient (CLUCOF) of a vertex v1 * CLUCOF in a graph quantifies how close the vertex and its neighbors are * to being a clique, a connected subgraph. * This is used to determine whether a graph is a small-world network. * @param v1 * @return */ float Graph::clusteringCoefficientLocal(const long int &v1){ if ( !graphModified() && (m_graph[ index [v1] ] -> hasCLC() ) ) { float clucof=m_graph[ index [v1] ] ->CLC(); qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") - " << " Not modified. Returning previous clucof = " << clucof; return clucof; } qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") - " << " Graph changed or clucof not calculated."; bool graphIsSymmetric = false; if ( graphSymmetric() ) { graphIsSymmetric = true; } else { graphIsSymmetric = false; } float clucof=0, denom = 0 , nom = 0; int u1 = 0 , u2 = 0, k = 0; H_StrToBool neighborhoodEdges; neighborhoodEdges.clear(); qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") - vertex " << v1 << "[" << index[v1] << "] " << " Checking adjacent edges " ; QHash *reciprocalEdges = new QHash; reciprocalEdges = m_graph [ index[v1] ] -> reciprocalEdgesHash(); QHash::const_iterator it1; QHash::const_iterator it2; it1=reciprocalEdges->cbegin(); while ( it1 != reciprocalEdges->cend() ) { u1 = it1.key(); qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << v1 << "<->" << u1 << "[" << index[u1] << "] exists" << "weight " << it1.value(); if ( v1 == u1 ) { qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "v1 == u1 - CONTINUE"; ++it1; continue; } it2=reciprocalEdges->cbegin(); qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "Checking if neighbor" << u1 << "is connected to other neighbors of" << v1; while ( it2 != reciprocalEdges->cend() ){ u2 = it2.key(); qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "Other neighbor" << u2 << "Check if there is an edge" << u1 << "[" << index[u1] << "]" << "->" << u2 ; if ( u1 == u2 ) { qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "u1 == u2 - CONTINUE"; ++it2; continue; } if ( edgeExists( u1, u2 ) != 0 ) { qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "Connected neighbors: " << u1 << " -> " << u2; QString edge = QString::number(u1) + "->" + QString::number(u2); QString revedge = QString::number(u2) + "->" + QString::number(u1); if ( graphIsSymmetric ) { if ( ! neighborhoodEdges.contains(edge) && ! neighborhoodEdges.contains(revedge) ) { neighborhoodEdges.insert(edge, true); qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "Edge added to neighborhoodEdges : " << edge; } else { qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "Edge not added, discovered previously : " << edge; } } else { if ( ! neighborhoodEdges.contains(edge) ) { neighborhoodEdges.insert(edge, true); qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "Edge added to neighborhoodEdges : " << edge; } else { qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "Edge not added, discovered previously : " << edge; } } } ++it2; } ++it1; } nom=neighborhoodEdges.count(); qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "neighborhoodEdges.count() =" << nom; if ( nom == 0) return 0; //stop if we're at a leaf. if ( graphIsSymmetric ){ k=reciprocalEdges->count(); //k_{i} is the number of neighbours of a vertex denom = k * (k -1.0) / 2.0; qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "Symmetric graph. " << "Max edges in neighborhood" << denom ; } else { // fixme : normally we should have a special method // to compute the number of vertices k_i = |N_i|, in the neighborhood N_i k=reciprocalEdges->count(); denom = k * (k -1.0); qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") - " << "Not symmetric graph. " << "Max edges in neighborhood" << denom ; } clucof = nom / denom; qDebug() << "Graph::clusteringCoefficientLocal("<< v1 << ") -" << "CLUCOF = "<< clucof; m_graph[ index [v1] ] -> setCLC(clucof); reciprocalEdges->clear(); neighborhoodEdges.clear(); return clucof; } /** * @brief Graph::clusteringCoefficient * Calculates local clustering coefficients and returns * the network average Clustering Coefficient * @param updateProgress * @return */ float Graph::clusteringCoefficient (const bool updateProgress){ qDebug("=== Graph::clusteringCoefficient() "); averageCLC=0; maxCLC=0; minCLC=1; float temp=0; float x=0; float N = vertices(); int progressCounter = 0; Q_UNUSED(progressCounter ); QList::const_iterator vertex; for ( vertex = m_graph.cbegin(); vertex != m_graph.cend(); ++vertex) { temp = clusteringCoefficientLocal( (*vertex)->name() ); if (temp > maxCLC) { maxCLC = temp; maxNodeCLC = (*vertex)->name(); } if ( temp < minCLC ) { minNodeCLC = (*vertex)->name(); minCLC= temp; } averageCLC += temp; if (updateProgress) emit updateProgressDialog(++progressCounter); } averageCLC = averageCLC / N ; qDebug() << "Graph::clusteringCoefficient() network average " << averageCLC; for ( vertex = m_graph.cbegin(); vertex != m_graph.cend(); ++vertex) { x = ( (*vertex)->CLC() - averageCLC ) ; x *=x; varianceCLC += x; } varianceIC /= N; return averageCLC; } /** * @brief Graph::graphTriadCensus * Conducts a triad census and updates QList::triadTypeFreqs, * which is the list carrying all triad type frequencies * Complexity:O(n!) * @return */ bool Graph::graphTriadCensus(){ int mut=0, asy=0, nul =0; int temp_mut=0, temp_asy=0, temp_nul =0, counter_021=0; int ver1, ver2, ver3; int progressCounter = 0; qDebug() << "Graph::graphTriadCensus()"; /* * QList::triadTypeFreqs stores triad type frequencies with the following order: * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 * 003 012 102 021D 021U 021C 111D 111U 030T 030C 201 120D 120U 120C 210 300 */ for (int i = 0; i <= 15; ++i) { triadTypeFreqs.append(0); qDebug() << " initializing triadTypeFreqs[" << i << "] = "<< triadTypeFreqs[i]; } QList::const_iterator v1; QList::const_iterator v2; QList::const_iterator v3; for (v1=m_graph.cbegin(); v1!=m_graph.cend(); v1++) { for (v2=(v1+1); v2!=m_graph.cend(); v2++) { ver1=(*v1)->name(); ver2=(*v2)->name(); temp_mut=0, temp_asy=0, temp_nul =0; if ( (*v1)->hasEdgeTo( ver2 ) ) { if ( (*v2)->hasEdgeTo( ver1 ) ) temp_mut++; else temp_asy++; } else if ( (*v2)->hasEdgeTo( ver1 ) ) temp_asy++; else temp_nul++; for (v3=(v2+1); v3!=m_graph.cend(); v3++){ mut = temp_mut ; asy = temp_asy ; nul = temp_nul ; ver3=(*v3)->name(); if ( (*v1)->hasEdgeTo( ver3 ) ) { if ( (*v3)->hasEdgeTo( ver1 ) ) mut++; else asy++; } else if ( (*v3)->hasEdgeTo( ver1 ) ) asy++; else nul++; if ( (*v2)->hasEdgeTo( ver3 ) ) { if ( (*v3)->hasEdgeTo( ver2 ) ) mut++; else asy++; } else if ( (*v3)->hasEdgeTo( ver2 ) ) asy++; else nul++; qDebug()<< "triad of ("<< ver1 << ","<< ver2 << ","<< ver3 << ") = (" < m_triad; bool isDown=false, isUp=false, isCycle=false, isTrans=false; bool isOutLinked=false, isInLinked=false; qDebug () << "Graph::triadType_examine_MAN_label() " << " adding ("<< vert1->name() << ","<< vert2->name() << ","<< vert3->name() << ") to m_triad "; m_triad<name() << ", "<< vert2->name()<< ", "<< vert3->name()<< " ) = (" <name() ; isOutLinked=false; isInLinked=false; foreach (Vertex *target, m_triad) { if ( source->name() == target->name() ) continue; if ( source->hasEdgeTo(target->name()) ){ if ( isOutLinked ){ triadTypeFreqs[3] ++;//"021D" break; } else if (isInLinked){ triadTypeFreqs[5] ++;//"021C" break; } else{ isOutLinked=true; } } else if( target->hasEdgeTo(source->name()) ){ // qDebug() << " Vertex " << source->name() << " is IN linked from " <name(); if ( isInLinked ){ triadTypeFreqs[4] ++;//"021U" break; } else if (isOutLinked){ triadTypeFreqs[5] ++;//"021C" break; } else{ isInLinked=true; } } } } break; case 3: qDebug() << "triad vertices: ( "<< vert1->name() << ", "<< vert2->name()<< ", "<< vert3->name()<< " ) = (" <name() ; isOutLinked=false; foreach (Vertex *target, m_triad) { if ( source->name() == target->name() ) continue; if ( source->hasEdgeTo(target->name()) ){ if ( isOutLinked ){ triadTypeFreqs[8] ++;//"030T" isTrans=true; break; } else{ isOutLinked=true; } } } } if ( ! isTrans ) {//"030C" triadTypeFreqs[9] ++; } break; } break; case 1: switch (asy){ case 0: //"102"; triadTypeFreqs[2] ++; break; case 1: isDown=false; isUp=false; //qDebug() << "triad vertices: ( "<< vert1->name() << ", "<< vert2->name()<< ", "<< vert3->name()<< " ) = (" <name() ; isInLinked=false; foreach (Vertex *target, m_triad) { if ( source->name() == target->name() ) continue; if ( target->hasEdgeTo(source->name()) ){ if ( isInLinked ){ triadTypeFreqs[6] ++;//"030T" isUp=true; break; } else{ isInLinked=true; } } } } if ( ! isUp ) {//"111U" triadTypeFreqs[7] ++; } break; case 2: isDown=false; isUp=false; isCycle=true; qDebug() << "triad vertices: ( "<< vert1->name() << ", " << vert2->name()<< ", "<< vert3->name()<< " ) = (" <name() ; isOutLinked=false; isInLinked=false; foreach (Vertex *target, m_triad) { if ( source->name() == target->name() ) continue; if ( source->hasEdgeTo(target->name()) ){ if (target->hasEdgeTo(source->name() ) ){ isInLinked=true; isOutLinked=true; continue; } else if ( isOutLinked && !isInLinked ){ triadTypeFreqs[11] ++;//"120D" isDown=true; isCycle=false; break; } else{ isOutLinked=true; } } else if( target->hasEdgeTo(source->name()) ){ // qDebug() << " Vertex " << source->name() << " is IN linked from " <name(); if (source->hasEdgeTo(target->name())){ isOutLinked=true; isInLinked=true; continue; } else if ( isInLinked && !isOutLinked ){ triadTypeFreqs[12] ++;//"120U" isUp=true; isCycle=false; break; } else{ isInLinked=true; } } } if (isUp || isDown) break; } if ( isCycle ) { //"120C" triadTypeFreqs[13] ++; } break; case 3: // nothing here! break; } break; case 2: switch (asy){ case 0: // "201" triadTypeFreqs[10] ++; break; case 1: // "210" triadTypeFreqs[14] ++; break; } break; case 3: // "300" if (asy==0 && nul==0) triadTypeFreqs[15] ++; break; } } /** Calculates and returns x! factorial... used in (n 2)p edges calculation */ int Graph:: factorial(int x) { int tmp; if(x <= 1) return 1; tmp = x * factorial(x - 1); return tmp; } /** * @brief Graph::graphName * If m_graphName is set on file loading, it returns that. * If m_graphName is empty, then returns current relation name * If m_graphName is empty and there is no current relation name, * then returns "noname" * @return */ QString Graph::graphName() const { if (m_graphName.isEmpty() ) { if ( !( relationCurrentName().isEmpty()) ) { return relationCurrentName(); } else { //TODO: Maybe we should use m_filename in this case? return "noname"; } } return m_graphName; } /** * @brief Graph::graphLoad * Our almost universal network loader. :) * It creates a new Parser object, * moves it to a another thread, * connects signals and slots and * calls its run() method. * @param m_fileName * @param m_codecName * @param m_showLabels * @param maxWidth * @param maxHeight * @param fileFormat * @param two_sm_mode * @return */ void Graph::graphLoad ( const QString m_fileName, const QString m_codecName, const bool m_showLabels, const int fileFormat, const int two_sm_mode, const QString delimiter){ initVertexLabelsVisibility = m_showLabels; qDebug() << "Graph::graphLoad() - clearing relations "; relationsClear(); qDebug() << "Graph::graphLoad() - "<< m_fileName << " calling parser.load() from thread " << this->thread(); Parser *file_parser = new Parser( m_fileName, m_codecName, initVertexSize, initVertexColor, initVertexShape, initVertexNumberColor, initVertexNumberSize, initVertexLabelColor, initVertexLabelSize, initEdgeColor, canvasWidth, canvasHeight, fileFormat, two_sm_mode, delimiter ); qDebug () << "Graph::graphLoad() - file_parser thread " << file_parser->thread() << " moving it to new thread "; file_parser->moveToThread(&file_parserThread); qDebug () << "Graph::graphLoad() - file_parser thread now " << file_parser->thread(); qDebug () << "Graph::graphLoad() - connecting file_parser signals "; connect(&file_parserThread, &QThread::finished, file_parser, &QObject::deleteLater); connect(file_parser, &Parser::addRelation, this, &Graph::relationAdd); connect ( file_parser, SIGNAL( relationSet (int) ), this, SLOT( relationSet (int) ) ) ; connect ( file_parser, SIGNAL( createNode (const int &,const int &, const QString &, const QString &, const int&, const QString &, const QString &, const int&, const QPointF&, const QString &, const bool &) ), this, SLOT( vertexCreate( const int &, const int &, const QString &, const QString &, const int &, const QString &, const QString &, const int &, const QPointF &, const QString &, const bool &) ) ) ; connect ( file_parser, SIGNAL (createNodeAtPosRandom(const bool &)), this, SLOT(vertexCreateAtPosRandom(const bool &)) ); connect ( file_parser, SIGNAL (createNodeAtPosRandomWithLabel( const int ,const QString &, const bool &)), this, SLOT(vertexCreateAtPosRandomWithLabel( const int &,const QString &, const bool &) ) ); connect ( file_parser, SIGNAL( edgeCreate (const int&, const int&, const float&, const QString&, const int&, const bool&, const bool&, const QString&, const bool&)), this, SLOT( edgeCreate (const int&, const int&, const float&, const QString&, const int&, const bool&, const bool&, const QString&, const bool&) ) ); connect ( file_parser, SIGNAL(networkFileLoaded(int, QString, QString, int, int, bool, const QString &) ), this, SLOT(graphFileLoaded( const int &, const QString &, const QString &, const int &, const int&, const bool&, const QString &) ) ); connect ( file_parser, SIGNAL(removeDummyNode(int)), this, SLOT (vertexRemoveDummyNode(int)) ); connect ( file_parser, &Parser::finished, this, &Graph::graphLoadedTerminateParserThreads ); qDebug() << "Graph::graphLoad() - Starting file_parserThread "; file_parserThread.start(); qDebug() << "Graph::graphLoad() - calling file_parser->run() "; file_parser->run(); } /** * @brief Graph::graphLoadedTerminateParserThreads * @param reason */ void Graph::graphLoadedTerminateParserThreads(QString reason) { qDebug() << "Graph::graphLoadedTerminateParserThreads() - reason " << reason <<" Checking if file_parserThread is running..."; if (file_parserThread.isRunning() ) { qDebug() << "Graph::graphLoadedTerminateParserThreads() - file_parserThread running." "Calling file_parserThread.quit();"; file_parserThread.quit(); qDebug() << "Graph::graphLoadedTerminateParserThreads() - deleting file_parser pointer"; delete file_parser; file_parser = 0; // see why here: https://goo.gl/tQxpGA } } /** * @brief Graph::graphFileLoaded * Updates MW with the loaded file type (0=nofile, 1=Pajek, 2=Adjacency etc) * Called from Parser on file parsing end or file error. * @param type * @param netName * @param aNodes * @param totalLinks * @param undirected */ void Graph::graphFileLoaded (const int &fileType, const QString &fName, const QString &netName, const int &totalNodes, const int &totalLinks, const bool &undirected, const QString &message) { if ( fileType == FILE_UNRECOGNIZED ) { qDebug() << "Graph::graphFileLoaded() - FILE_UNRECOGNIZED. " "Emitting signalGraphLoaded with error message " << message; emit signalGraphLoaded (fileType, QString::null, QString::null, 0, 0, message); return; } fileName = fName; if (netName != "") m_graphName=netName ; else m_graphName=(fileName.split("/").last()).split("/").first(); m_undirected = undirected; m_fileFormat = fileType; qDebug() << "Graph::graphFileLoaded() - " << " type " << fileType << " filename " << fileName << " name " << graphName() << " nodes " << totalNodes << " links " << totalLinks << " undirected " << undirected; graphModifiedSet(GRAPH_CHANGED_NEW); emit signalGraphLoaded (fileType, fileName, graphName(), totalNodes, totalLinks, message); graphModifiedSet(GRAPH_CHANGED_NONE); qDebug ()<< "Graph::graphFileLoaded() -check parser if running..."; } /** * @brief graphFileFormat * @return * Returns the format of the last file opened */ int Graph::graphFileFormat() const { return m_fileFormat; } /** * @brief Graph::graphFileFormatExportSupported * @param fileFormat * @return */ bool Graph::graphFileFormatExportSupported(const int &fileFormat) const { if (m_graphFileFormatExportSupported.contains(fileFormat)) { return true; } return false; } /** * @brief Graph::graphSave * Our almost universal graph saver. :) * Actually it just checks the requested file type and * calls the right saveGraphTo...() method * @param fileName * @param fileType * @return */ void Graph::graphSave(const QString &fileName, const int &fileType , const bool &saveEdgeWeights) { qDebug() << "Graph::graphSave()"; bool saved = false; m_fileFormat = fileType; switch (fileType) { case FILE_PAJEK : { qDebug() << "Graph::graphSave() - Pajek formatted file"; saved=graphSaveToPajekFormat(fileName, graphName(), canvasWidth, canvasHeight) ; break; } case FILE_ADJACENCY: { qDebug() << "Graph::graphSave() - Adjacency formatted file"; saved=graphSaveToAdjacencyFormat(fileName, saveEdgeWeights) ; break; } case FILE_GRAPHVIZ: { qDebug() << "Graph::graphSave() - GraphViz/Dot formatted file"; saved=graphSaveToDotFormat(fileName); } case FILE_GRAPHML: { // GraphML qDebug() << "Graph::graphSave() - GraphML formatted file"; saved=graphSaveToGraphMLFormat(fileName); break; } default: { m_fileFormat = FILE_UNRECOGNIZED; qDebug() << "Graph::graphSave() - Error! Unrecognized fileType"; break; } }; if (saved) { if (graphModified()) { calculatedGraphWeighted = false; calculatedGraphDensity = false; calculatedEdges = false; calculatedVertices = false; calculatedVerticesList=false; calculatedVerticesSet = false; calculatedGraphSymmetry = false; calculatedIsolates = false; calculatedAdjacencyMatrix = false; calculatedDistances = false; calculatedCentralities = false; calculatedDP = false; calculatedDC = false; calculatedPP = false; calculatedIRCC = false; calculatedIC = false; calculatedPRP = false; } graphModifiedSet(GRAPH_CHANGED_NONE); signalGraphSaved(fileType); } else { signalGraphSaved(0); } } /** Saves the active graph to a Pajek-formatted file Preserves node properties (positions, colours, etc) */ bool Graph::graphSaveToPajekFormat (const QString &fileName, \ QString networkName, int maxWidth, int maxHeight ) { float weight=0; QFileInfo fileInfo (fileName); QString fileNameNoPath = fileInfo.fileName(); networkName = (networkName == "") ? graphName().toHtmlEscaped(): networkName; networkName = (networkName == "unnamed") ? fileNameNoPath.toHtmlEscaped().left(fileNameNoPath.lastIndexOf('.')): networkName; qDebug () << " Graph::graphSaveToPajekFormat() - file: " << fileName.toUtf8() << "networkName" << networkName; maxWidth = (maxWidth == 0) ? canvasWidth:maxWidth ; maxHeight= (maxHeight== 0) ? canvasHeight:maxHeight; QFile f( fileName ); if ( !f.open( QIODevice::WriteOnly | QIODevice::Text ) ) { emit statusMessage ( tr("Error. Could not write to ") + fileName ); return false; } QTextStream t( &f ); t.setCodec("UTF-8"); t<<"*Network "<::const_iterator it; QList::const_iterator jt; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ qDebug()<<" Name x "<< (*it)->name() ; t<<(*it)->name() <<" "<<"\""<<(*it)->label()<<"\"" ; t << " ic "; t<< (*it)->colorToPajek(); qDebug()<<" Coordinates x " << (*it)->x()<< " "<y()<< " "<x()/(maxWidth)<<" \t"<<(*it)->y()/(maxHeight); t << "\t"<<(*it)->shape(); t<<"\n"; } t<<"*Arcs \n"; qDebug()<< "Graph::graphSaveToPajekFormat: Arcs"; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ for (jt=m_graph.begin(); jt!=m_graph.end(); jt++){ qDebug() << "Graph::graphSaveToPajekFormat: it=" << (*it)->name() << ", jt=" << (*jt)->name() ; if ( (weight=edgeExists ( (*it)->name(), (*jt)->name())) !=0 && ( edgeExists ((*jt)->name(), (*it)->name())) != weight ) { qDebug()<<"Graph::graphSaveToPajekFormat weight "<< weight << " color "<< (*it)->outLinkColor( (*jt)->name() ) ; t << (*it)->name() <<" "<<(*jt)->name()<< " "<outLinkColor( (*jt)->name() ); t <<"\n"; } } } t<<"*Edges \n"; qDebug() << "Graph::graphSaveToPajekFormat: Edges"; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ for (jt=m_graph.begin(); jt!=m_graph.end(); jt++){ qDebug() << "Graph::graphSaveToPajekFormat: it=" << (*it)->name() << ", jt=" <<(*jt)->name() ; if ( ( weight=edgeExists((*it)->name(), (*jt)->name(), true) )!=0 ) { if ( (*it)->name() > (*jt)->name() ) continue; t << (*it)->name() <<" "<<(*jt)->name()<< " "<outLinkColor( (*jt)->name() ); t <<"\n"; } } } f.close(); emit statusMessage (tr( "File %1 saved" ).arg( fileNameNoPath )); return true; } /** * @brief Graph::graphSaveToAdjacencyFormat * @param fileName * @return */ bool Graph::graphSaveToAdjacencyFormat (const QString &fileName, const bool &saveEdgeWeights){ QFile file( fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { emit statusMessage ( tr("Error. Could not write to ") + fileName ); return false; } QTextStream outText( &file ); outText.setCodec("UTF-8"); qDebug("Graph: graphSaveToAdjacencyFormat() for %i vertices", vertices()); writeMatrixAdjacencyTo(outText, saveEdgeWeights); file.close(); QString fileNameNoPath=fileName.split("/").last(); emit statusMessage (QString( tr("Adjacency matrix-formatted network saved into file %1") ).arg( fileNameNoPath )); return true; } bool Graph::graphSaveToDotFormat (QString fileName) { Q_UNUSED(fileName); return true; } bool Graph::graphSaveToGraphMLFormat (const QString &fileName, QString networkName, int maxWidth, int maxHeight) { float weight=0; int source=0, target=0, edgeCount=0, m_size=1, m_labelSize; QString m_color, m_labelColor, m_label; bool openToken; QFileInfo fileInfo (fileName); QString fileNameNoPath = fileInfo.fileName(); networkName = (networkName == "") ? graphName().toHtmlEscaped(): networkName; networkName = (networkName == "unnamed") ? fileNameNoPath.toHtmlEscaped().left(fileNameNoPath.lastIndexOf('.')): networkName; qDebug () << "Graph::graphSaveToGraphMLFormat() - file:" << fileName.toUtf8() << "networkName"<< networkName; maxWidth = (maxWidth == 0) ? canvasWidth:maxWidth ; maxHeight= (maxHeight== 0) ? canvasHeight:maxHeight; QFile f( fileName ); if ( !f.open( QIODevice::WriteOnly | QIODevice::Text ) ) { emit statusMessage ( tr("Error. Could not write to ") + fileName ); return false; } QTextStream outText( &f ); outText.setCodec("UTF-8"); qDebug () << "Graph::graphSaveToGraphMLFormat() - codec used for saving stream: " << outText.codec()->name(); qDebug()<< "Graph::graphSaveToGraphMLFormat() - writing xml version"; outText << "name() << "\"?> \n"; outText << " \n" ; outText << "" "\n"; qDebug()<< "Graph::graphSaveToGraphMLFormat() - writing keys "; outText << " \n" " " " \n" " \n"; outText << " \n" " " << "0.0" << " \n" " \n"; outText << " \n" " " << "0.0" << " \n" " \n"; outText << " \n" " "<< initVertexSize << " \n" " \n"; outText << " \n" " " << initVertexColor << " \n" " \n"; outText << " \n" " " << initVertexShape << " \n" " \n"; outText << " \n" " " << initVertexLabelColor << " \n" " \n"; outText << " \n" " " << initVertexLabelSize << " \n" " \n"; outText << " \n" " 1.0 \n" " \n"; outText << " \n" " " << initEdgeColor << " \n" " \n"; outText << " \n" " " << ""<< " \n" " \n"; QList::const_iterator it; QList::const_iterator jt; QString relationName; int relationPrevious = relationCurrent(); for (int i = 0; i < relations(); ++i) { relationName = (m_relationsList.at(i).simplified()).remove("\""); relationSet( i , false); qDebug()<< "Graph::graphSaveToGraphMLFormat() - writing graph tag. Relation" << relationName ; if (graphUndirected()) outText << " \n"; else outText << " \n"; qDebug()<< "Graph::graphSaveToGraphMLFormat() - writing nodes data"; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled () ) continue; qDebug() << "Graph::graphSaveToGraphMLFormat() - Node id: " << (*it)->name() ; outText << " name() << "\"> \n"; m_color = (*it)->color(); m_size = (*it)->size() ; m_labelSize=(*it)->labelSize() ; m_labelColor=(*it)->labelColor() ; m_label=(*it)->label(); m_label = htmlEscaped(m_label); outText << " " << m_label <<"\n"; qDebug()<<"Graph::graphSaveToGraphMLFormat() - Coordinates x " << (*it)->x()<< " "<y()<< " "<" << (*it)->x()/(maxWidth) <<"\n"; outText << " " << (*it)->y()/(maxHeight) <<"\n"; if ( initVertexSize != m_size ) { outText << " " << m_size <<"\n"; } if ( QString::compare ( initVertexColor, m_color, Qt::CaseInsensitive) != 0) { outText << " " << m_color <<"\n"; } outText << " " << (*it)->shape() <<"\n"; if ( QString::compare ( initVertexLabelColor, m_labelColor, Qt::CaseInsensitive) != 0) { outText << " " << m_labelColor <<"\n"; } if ( initVertexLabelSize != m_labelSize ) { outText << " " << m_labelSize <<"\n"; } outText << " \n"; } qDebug() << "Graph::graphSaveToGraphMLFormat() - writing edges data"; edgeCount=0; if (!graphUndirected()) { for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { for (jt=m_graph.begin(); jt!=m_graph.end(); jt++) { source=(*it)->name(); target=(*jt)->name(); m_label = ""; weight= edgeExists( source,target ) ; if ( weight !=0 ) { ++edgeCount; m_color = (*it)->outLinkColor( target ); m_label = edgeLabel(source, target); m_label=htmlEscaped(m_label); qDebug()<< "Graph::graphSaveToGraphMLFormat() - edge no " << edgeCount << " from n1=" << source << " to n2=" << target << " with weight " << weight << " and color " << m_color.toUtf8() ; outText << " \n"; outText << " " << weight<<"" <<" \n"; openToken=false; } if ( QString::compare ( initEdgeColor, m_color, Qt::CaseInsensitive) != 0) { if (openToken) outText << "> \n"; outText << " " << m_color <<"" <<" \n"; openToken=false; } if ( !m_label.isEmpty()) { if (openToken) outText << "> \n"; outText << " " << m_label<<"" <<" \n"; openToken=false; } if (openToken) outText << "/> \n"; else outText << " \n"; } } } } else { for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it) { for (jt=it; jt!=m_graph.end(); jt++) { source=(*it)->name(); target=(*jt)->name(); weight= edgeExists( source,target ); m_label = ""; if ( weight !=0 ) { ++edgeCount; m_color = (*it)->outLinkColor( target ); m_label = edgeLabel(source, target); m_label=htmlEscaped(m_label); qDebug()<< "Graph::graphSaveToGraphMLFormat() - edge no " << edgeCount << " from n1=" << source << " to n2=" << target << " with weight " << weight << " and color " << m_color.toUtf8() ; outText << " \n"; outText << " " << weight<<"" <<" \n"; openToken=false; } if ( QString::compare ( initEdgeColor, m_color, Qt::CaseInsensitive) != 0) { if (openToken) outText << "> \n"; outText << " " << m_color <<"" <<" \n"; openToken=false; } if ( !m_label.isEmpty()) { if (openToken) outText << "> \n"; outText << " " << m_label<<"" <<" \n"; openToken=false; } if (openToken) outText << "/> \n"; else outText << " \n"; } } } } outText << " \n"; } outText << "\n"; f.close(); relationSet(relationPrevious, false); emit statusMessage( tr( "File %1 saved" ).arg( fileNameNoPath ) ); return true; } /** * @brief Graph::writeDataSetToFile * Writes a known dataset to the given file * @param fileName */ void Graph::writeDataSetToFile (const QString dir, const QString fileName) { qDebug() << "Graph::writeDataSetToFile() to " << dir+fileName; QFile file( dir+fileName ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { emit statusMessage ( tr("Error. Could not write to ") + fileName ); return; } QTextStream outText( &file ); outText.setCodec("UTF-8"); QString datasetDescription=QString::null; qDebug()<< " ... writing dataset "; if ( fileName == "Herschel_Graph.paj") { qDebug()<< " ... to " << fileName; datasetDescription = tr("The Herschel graph is the smallest nonhamiltonian " "polyhedral graph. \n" "It is the unique such graph on 11 nodes, " "and has 18 edges."); outText << "*Network Herschel_Graph" << endl << "*Vertices 11" << endl << "1 \"1\" ic red 0.48225 0.411308 circle" << endl << "2 \"2\" ic red 0.652297 0.591389 circle" << endl << "3 \"3\" ic red 0.479571 0.762504 circle"<< endl << "4 \"4\" ic red 0.849224 0.41395 circle"<< endl << "5 \"5\" ic red 0.48196 0.06 circle"<< endl << "6 \"6\" ic red 0.148625 0.413208 circle"<< endl << "7 \"7\" ic red 0.654193 0.198133 circle"<< endl << "8 \"8\" ic red 0.268771 0.593206 circle"<< endl << "9 \"9\" ic red 0.272785 0.19606 circle"<< endl << "10 \"10\" ic red 0.834746 0.0533333 circle"<< endl << "11 \"11\" ic red 0.134137 0.761837 circle"<< endl << "*Arcs "<< endl << "*Edges "<< endl << "1 3 1 c #616161"<< endl << "1 4 1 c #616161"<< endl << "1 5 1 c #616161"<< endl << "1 6 1 c #616161"<< endl << "2 3 1 c #616161"<< endl << "2 4 1 c #616161"<< endl << "2 7 1 c #616161"<< endl << "2 8 1 c #616161"<< endl << "3 11 1 c #616161"<< endl << "4 10 1 c #616161"<< endl << "5 9 1 c #616161"<< endl << "5 10 1 c #616161"<< endl << "6 9 1 c #616161"<< endl << "6 11 1 c #616161"<< endl << "7 9 1 c #616161"<< endl << "7 10 1 c #616161"<< endl << "8 9 1 c #616161"<< endl << "8 11 1 c #616161"; } else if ( fileName == "Krackhardt_High-tech_managers.paj" ) { qDebug()<< " ... to " << fileName; datasetDescription = tr("Krackhardt's High-tech Managers is a famous social network " "of 21 managers of a high-tech US company. \n\n" "The company manufactured high-tech equipment " "and had just over 100 employees with 21 managers. " "David Krackhardt collected the data to assess the effects " "of a recent management intervention program. \n\n" "The network consists of 3 relations:\n" "- Advice\n" "- Friendship\n" "- Reports To\n" "Each manager was asked to whom do you go to for advice and who is your friend. " "Data for the \"whom do you report\" relation was taken from company documents. \n\n" "This data is used by Wasserman and Faust in their seminal network analysis book.\n\n" "Krackhardt D. (1987). Cognitive social structures. Social Networks, 9, 104-134."); outText << "*Network Krackhardt's High-tech managers"<< endl << "*Vertices 21"<< endl << "1 \"v1\" 0.6226 0.7207" << endl << "2 \"v2\" 0.6000 0.5533" << endl << "3 \"v3\" 0.6722 0.3928" << endl << "4 \"v4\" 0.7646 0.6000" << endl << "5 \"v5\" 0.3518 0.4775" << endl << "6 \"v6\" 0.7583 0.0784" << endl << "7 \"v7\" 0.6692 0.2475" << endl << "8 \"v8\" 0.7349 0.5030" << endl << "9 \"v9\" 0.5325 0.3892" << endl << "10 \"v10\" 0.5846 0.6311" << endl << "11 \"v11\" 0.4600 0.4733" << endl << "12 \"v12\" 0.8855 0.2566" << endl << "13 \"v13\" 0.1145 0.4786" << endl << "14 \"v14\" 0.3838 0.3270" << endl << "15 \"v15\" 0.5349 0.4455" << endl << "16 \"v16\" 0.6117 0.9216" << endl << "17 \"v17\" 0.7041 0.4144" << endl << "18 \"v18\" 0.4864 0.5808" << endl << "19 \"v19\" 0.5728 0.4802" << endl << "20 \"v20\" 0.6640 0.5041" << endl << "21 \"v21\" 0.7846 0.3329" << endl << "*Matrix :1 gives_advice_to"<< endl << "0 1 0 1 0 0 0 1 0 0 0 0 0 0 0 1 0 1 0 0 1" << endl << "0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1" << endl << "1 1 0 1 0 1 1 1 1 1 1 1 0 1 0 0 1 1 0 1 1" << endl << "1 1 0 0 0 1 0 1 0 1 1 1 0 0 0 1 1 1 0 1 1" << endl << "1 1 0 0 0 1 1 1 0 1 1 0 1 1 0 1 1 1 1 1 1" << endl << "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1" << endl << "0 1 0 0 0 1 0 0 0 0 1 1 0 1 0 0 1 1 0 0 1" << endl << "0 1 0 1 0 1 1 0 0 1 1 0 0 0 0 0 0 1 0 0 1" << endl << "1 1 0 0 0 1 1 1 0 1 1 1 0 1 0 1 1 1 0 0 1" << endl << "1 1 1 1 1 0 0 1 0 0 1 0 1 0 1 1 1 1 1 1 0" << endl << "1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0" << endl << "0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1" << endl << "1 1 0 0 1 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 0" << endl << "0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 1" << endl << "1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1" << endl << "1 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0" << endl << "1 1 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1" << endl << "1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 0 0 1 1 1" << endl << "1 1 1 0 1 0 1 0 0 1 1 0 0 1 1 0 0 1 0 1 0" << endl << "1 1 0 0 0 1 0 1 0 0 1 1 0 1 1 1 1 1 0 0 1" << endl << "0 1 1 1 0 1 1 1 0 0 0 1 0 1 0 0 1 1 0 1 0"<< endl << "*Matrix :2 is_friend_of" <"; switch (matrix) { case MATRIX_ADJACENCY: outText << tr("ADJACENCY MATRIX REPORT"); break; case MATRIX_LAPLACIAN: outText << tr("LAPLACIAN MATRIX REPORT"); break; case MATRIX_DEGREE: outText << tr("DEGREE MATRIX REPORT"); break; case MATRIX_DISTANCES: outText << tr("DISTANCES MATRIX REPORT"); break; case MATRIX_GEODESICS: outText << tr("GEODESICS MATRIX REPORT"); break; case MATRIX_ADJACENCY_INVERSE: outText << tr("INVERSE ADJACENCY MATRIX REPORT"); break; case MATRIX_REACHABILITY: outText << tr("REACHABILITY MATRIX REPORT"); break; case MATRIX_ADJACENCY_TRANSPOSE: outText << tr("TRANSPOSE OF ADJACENCY MATRIX REPORT"); break; case MATRIX_COCITATION: outText << tr("COCITATION MATRIX REPORT"); break; case MATRIX_DISTANCES_EUCLIDEAN: outText << tr("EUCLIDEAN DISTANCE MATRIX REPORT"); break; case MATRIX_DISTANCES_HAMMING: outText << tr("HAMMING DISTANCE MATRIX REPORT"); break; case MATRIX_DISTANCES_JACCARD: outText << tr("JACCARD DISTANCE MATRIX REPORT"); break; case MATRIX_DISTANCES_MANHATTAN: outText << tr("MANHATTAN DISTANCE MATRIX REPORT"); break; default: break; } outText << ""; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; switch (matrix) { case MATRIX_ADJACENCY: outText << "

" << tr("The adjacency matrix, AM, of a social network is a NxN matrix ") << tr("where each element (i,j) is the value of the edge from " "actor i to actor j, or 0 if no edge exists.") << "
" << "

"; AM.printHTMLTable(outText,true); break; case MATRIX_LAPLACIAN: outText << "

" << tr("The laplacian matrix L of a social network is a NxN matrix ") << tr("with L = D - A, where D the degree matrix and A the " "adjacency matrix. ") << "
" << tr("The elements of L are: " "
" "- Li,j = di, if i = j,
" "- Li,j = -1, if i ≠ j and there is an edge (i,j)
" "- and all other elements zero.
") << "
" << "

"; AM.laplacianMatrix().printHTMLTable(outText,true,false,false); break; case MATRIX_DEGREE: outText << "

" << tr("The degree matrix D of a social network is a NxN matrix ") << tr("where each element (i,i) is the degree of actor i " "and all other elements are zero.") << "
" << "

"; AM.degreeMatrix().printHTMLTable(outText, true); break; case MATRIX_DISTANCES: outText << "

" << tr("The distance matrix, DM, of a social network is a NxN matrix " "where each element (i,j) is the geodesic distance " "(length of shortest path) from actor i to actor j, " "or infinity if no shortest path exists.") << "
" << "

"; DM.printHTMLTable(outText,true); break; case MATRIX_GEODESICS: outText << "

" << tr("The geodesics matrix of a social network is a NxN matrix ") << tr("where each element (i,j) is the number of geodesics " "(shortest paths) from actor i to actor j, " "or infinity if no shortest path exists.") << "
" << "

"; TM.printHTMLTable(outText,true); break; case MATRIX_ADJACENCY_INVERSE: if (!inverseResult) { outText << "

" << tr("The adjacency matrix is singular.") << "
" << "

"; }else { invAM.printHTMLTable(outText,true); } break; case MATRIX_REACHABILITY: outText << "

" << tr("The reachability matrix R of a social network is a NxN matrix " "where each element R(i,j) is 1 if actors j is reachable from i " "otherwise 0.
" "Two nodes are reachable if there is a walk between them " "(their geodesic distance is non-zero).
" "Essentially the reachability matrix is a dichotomized " "geodesics matrix.") << "
" << "

"; XRM.printHTMLTable(outText,true); break; case MATRIX_ADJACENCY_TRANSPOSE: outText << "

" << tr("The adjacency matrix AM of a social network is a NxN matrix " "where each element (i,j) is the value of the edge from " "actor i to actor j, or 0 if no edge exists. ") << "
" << tr("This is the transpose of the adjacency matrix, AMT, " "a matrix whose (i,j) element is the (j,i) element of AM.") << "

"; AM.transpose().printHTMLTable(outText,true); break; case MATRIX_COCITATION: outText << "

" << tr("The Cocitation matrix, C = AT * A, is a " "NxN matrix where each element (i,j) is the number of " "actors that have outbound ties/links to both actors i and j.") << "
" << tr("The diagonal elements, Cii, of the Cocitation " "matrix are equal to the number of inbound edges of i (inDegree).") << "
" << tr("C is a symmetric matrix.") << "

"; AM.cocitationMatrix().printHTMLTable(outText,true); break; case MATRIX_DISTANCES_EUCLIDEAN: outText << "

" << tr("The Euclidean distances matrix is a " "NxN matrix where each element (i,j) is the Euclidean distance" "of the tie profiles between actors i and j, namely the " "square root of the sum of their squared differences.") << "
" << "

"; AM.distancesMatrix(METRIC_EUCLIDEAN_DISTANCE, varLocation, false, true ).printHTMLTable(outText,true); break; case MATRIX_DISTANCES_HAMMING: outText << "

" << tr("The Hamming distances matrix is a " "NxN matrix where each element (i,j) is the Hamming distance" "of the tie profiles between actors i and j, namely the " "number of different ties to other actors.") << "
" << "

"; AM.distancesMatrix(METRIC_HAMMING_DISTANCE, varLocation, false, true ).printHTMLTable(outText,true); break; case MATRIX_DISTANCES_JACCARD: outText << "

" << tr("The Jaccard distances matrix is a " "NxN matrix where each element (i,j) is the Jaccard distance" "of the tie profiles between actors i and j.") << "
" << "

"; AM.distancesMatrix(METRIC_JACCARD_INDEX, "Rows", false, true ).printHTMLTable(outText,true); break; case MATRIX_DISTANCES_MANHATTAN: outText << "

" << tr("The Manhattan distances matrix is a " "NxN matrix where each element (i,j) is the Manhattan distance" "of the tie profiles between actors i and j, namely the " "sum of their absolute differences.") << "
" << "

"; AM.distancesMatrix(METRIC_MANHATTAN_DISTANCE, varLocation, false, true ).printHTMLTable(outText,true); break; case MATRIX_DISTANCES_CHEBYSHEV: outText << "

" << tr("The Chebyshev distances matrix is a " "NxN matrix where each element (i,j) is the Chebyshev distance" "of the tie profiles between actors i and j, namely the greatest of their differences.") << "
" << "

"; AM.distancesMatrix(METRIC_CHEBYSHEV_MAXIMUM, varLocation, false, true ).printHTMLTable(outText,true); break; default: break; } outText << "

 

"; outText << "

"; outText << tr("Matrix report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** Exports the adjacency matrix to a given textstream */ void Graph::writeMatrixAdjacencyTo(QTextStream& os, const bool &saveEdgeWeights){ qDebug("Graph: adjacencyMatrix(), writing matrix with %i vertices", vertices()); QList::const_iterator it, it1; float weight=RAND_MAX; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ) continue; for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ if ( ! (*it1)->isEnabled() ) continue; if ( (weight = edgeExists( (*it)->name(), (*it1)->name() ) ) !=0 ) { //os << static_cast (weight) << " "; os << ((saveEdgeWeights) ? weight : 1 ) << " "; } else os << "0 "; } os << endl; } } /** Writes the adjacency matrix of G to a specified file fn */ void Graph::writeMatrixAdjacency (const QString fn, const bool &markDiag) { QTime computationTimer; computationTimer.start(); qDebug()<<"Graph::writeMatrixAdjacency() to : " << fn; QFile file( fn ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { qDebug()<< "Error opening file!"; emit statusMessage ( tr("Error. Could not write to ") + fn ); return; } QTextStream outText( &file ); outText.setCodec("UTF-8"); int sum=0; float weight=0; int rowCount=0; int N = vertices(); //int colCount=0; QList::const_iterator it, it1; outText << htmlHead; outText << "

"; outText << tr("ADJACENCY MATRIX"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("The adjacency matrix of a social network is a NxN matrix ") << tr("where each element (i,j) is the value of the edge from " "actor i to actor j, or 0 if no edge exists.") << "
" << "

"; outText << "" << "" << "" << ""; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ outText <<""; } outText << "" << "" << ""; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ) continue; rowCount++; outText << ""; outText <<""; for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ if ( ! (*it1)->isEnabled() ) continue; outText <<"name() ==(*it1)->name() )? " class=\"diag\">" : ">"); if ( (weight = edgeExists ( (*it)->name(), (*it1)->name() ) )!=0 ) { sum++; outText << (weight); } else { outText << 0 ; } outText << ""; } outText <<""; emit updateProgressDialog(rowCount); } outText << "
" << tr("Actor/Actor") << "" << (*it)->name() << "
" << (*it)->name() << "
"; qDebug("Graph: Found a total of %i edge",sum); if ( sum != edgesEnabled() ) qDebug ("Error in edge count found!!!"); else qDebug("Edge count OK!"); outText << "

 

"; outText << "

"; outText << tr("Adjacency matrix report,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /** Writes a visual representation of the adjacency matrix of G to a specified file fn This is called by MainWindow::slotViewAdjacencyMatrixPlotText() The resulting matrix HAS NO spaces between elements. */ void Graph::writeMatrixAdjacencyPlot (const QString fn, const bool &simpler) { QTime computationTimer; computationTimer.start(); qDebug()<<"Graph::writeMatrixAdjacencyPlot() to : " << fn; QFile file( fn ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { emit statusMessage ( tr("Error. Could not write to ") + fn ); return; } QTextStream outText( &file ); outText.setCodec("UTF-8"); int sum=0; float weight=0; int rowCount=0; int N = vertices(); QList::const_iterator it, it1; if (!simpler) { outText << htmlHead; } else outText << htmlHeadLight; outText << "

"; outText << tr("ADJACENCY MATRIX PLOT"); outText << "

"; outText << "

" << "" << tr("Network name: ") <<"" << graphName() <<"
" << "" << tr("Actors: ") <<"" << N << "

"; outText << "

" << tr("This a plot of the network's adjacency matrix, a NxN matrix ") << tr("where each element (i,j) is filled if there is an edge from " "actor i to actor j, or not filled if no edge exists.") << "
" << "

"; if (!simpler) { outText << ""; outText << ""; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ) continue; rowCount++; outText << ""; emit updateProgressDialog(rowCount); for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ if ( ! (*it1)->isEnabled() ) continue; if ( (weight = edgeExists ( (*it)->name(), (*it1)->name() ) )!=0 ) { sum++; outText <<""; } else { outText <<""; } } outText <<""; } outText << "
" << QString("\xe2\x96\xa0") << "" // << "  " << QString("\xe2\x96\xa1") << "
"; } else { outText << "

"; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() ) continue; rowCount++; emit updateProgressDialog(rowCount); for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ if ( ! (*it1)->isEnabled() ) continue; if ( (weight = edgeExists ( (*it)->name(), (*it1)->name() ) )!=0 ) { sum++; outText << QString("\xe2\x96\xa0") << " "; } else { outText << QString("\xe2\x96\xa1") << " "; } } outText << "
"<"; } qDebug("Graph: Found a total of %i edge",sum); if ( sum != edgesEnabled() ) qDebug ("Error in edge count found!!!"); else qDebug("Edge count OK!"); outText << "

 

"; outText << "

"; outText << tr("Adjacency matrix plot,
"); outText << tr("Created by Social Network Visualizer v%1: %2") .arg(VERSION).arg( actualDateTime.currentDateTime().toString ( QString ("ddd, dd.MMM.yyyy hh:mm:ss")) ) ; outText << "
"; outText << tr("Computation time: %1 msecs").arg( computationTimer.elapsed() ); outText << "

"; outText << htmlEnd; file.close(); } /* * Creates an adjacency matrix AM * where AM(i,j)=1 if i is connected to j * and AM(i,j)=0 if i not connected to j * * Used in Graph::centralityInformation(), Graph::graphWalksMatrixCreate * and Graph::graphMatrixAdjacencyInvert() */ void Graph::graphMatrixAdjacencyCreate(const bool dropIsolates, const bool considerWeights, const bool inverseWeights, const bool symmetrize ){ qDebug() << "Graph::graphMatrixAdjacencyCreate()"; float m_weight=RAND_MAX; int i=0, j=0; int N = vertices(); if (dropIsolates){ qDebug() << "Graph::graphMatrixAdjacencyCreate() - Find and drop possible isolates"; int m = N- verticesListIsolated().count(); AM.resize( m , m); } else AM.resize(N, N); QList::const_iterator it, it1; //qDebug() << "Graph::graphMatrixAdjacencyCreate() - creating new adjacency matrix "; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() || ( (*it)->isIsolated() && dropIsolates) ) { continue; } j=i; for (it1=it; it1!=m_graph.end(); it1++){ if ( ! (*it1)->isEnabled() || ( (*it1)->isIsolated() && dropIsolates) ) { continue; } if ( (m_weight = edgeExists ( (*it)->name(), (*it1)->name() ) ) !=0 ) { if (!considerWeights) { AM.setItem(i,j, 1 ); } else { if (inverseWeights) AM.setItem(i,j, 1.0 / m_weight ); else AM.setItem(i,j, m_weight ); } } else{ AM.setItem(i,j, 0); } qDebug()<<" AM("<< i+1 << ","<< j+1 << ") = " << AM.item(i,j); if (i != j ) { if ( (m_weight = edgeExists ( (*it1)->name(), (*it)->name() ) ) !=0 ) { if (!considerWeights) AM.setItem(j,i, 1 ); else { if (inverseWeights) AM.setItem(j,i, 1.0 / m_weight ); else AM.setItem(j,i, m_weight ); } if (symmetrize && (AM.item(i,j) != AM.item(j,i)) ) { AM.setItem(i,j, AM.item(j,i) ); } } else { AM.setItem(j,i, 0); if (symmetrize && (AM.item(i,j) != AM.item(j,i)) ) AM.setItem(j,i, AM.item(i,j) ); } qDebug()<<" AM("<< j+1 << ","<< i+1 << ") = " << AM.item(j,i); } j++; } i++; } calculatedAdjacencyMatrix=true; } bool Graph::graphMatrixAdjacencyInvert(const QString &method){ qDebug()<<"Graph::graphMatrixAdjacencyInvert() "; bool considerWeights=false; long int i=0, j=0; bool isSingular=true; int m = vertices(); int isolatedVertices = verticesListIsolated().count(); bool dropIsolates=true; // always drop isolates else AM will be singular graphMatrixAdjacencyCreate(dropIsolates, considerWeights); m-=isolatedVertices; invAM.resize(m,m); if ( method == "gauss") { invAM.inverseByGaussJordanElimination(AM); } else { invAM.inverse(AM); } QList::const_iterator it, it1; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() || (*it)->isIsolated()) continue; j=0; for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ if ( ! (*it1)->isEnabled() || (*it)->isIsolated() ) continue; if ( invAM.item(i,j) != 0) isSingular = false; j++; } i++; } return !isSingular; } void Graph::writeMatrixAdjacencyInvert(const QString &fn, const QString &method) { qDebug("Graph::writeMatrixAdjacencyInvert() "); int i=0, j=0; QList::const_iterator it, it1; QFile file( fn ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { emit statusMessage ( tr("Error. Could not write to ") + fn ); return; } QTextStream outText( &file ); outText.setCodec("UTF-8"); outText << "-Social Network Visualizer "<< VERSION < 0 ) outText << endl<< "Dropped "<< isolatedVertices << " isolated vertices" << endl<< endl; for (it=m_graph.cbegin(); it!=m_graph.cend(); ++it){ if ( ! (*it)->isEnabled() || (*it)->isIsolated() ) continue; j=0; for (it1=m_graph.cbegin(); it1!=m_graph.cend(); ++it1){ if ( ! (*it1)->isEnabled() || (*it)->isIsolated() ) continue; outText << invAM.item(i,j)<< " "; qDebug() << i << "," << j << " = " << invAM.item(i,j); j++; } i++; outText << endl; qDebug() << endl; } file.close(); } /** * @brief Computes the Degree matrix of the graph and writes it to given file * @param fn */ void Graph::writeMatrixDegreeText(const QString &fn) { qDebug("Graph::writeMatrixDegreeText() "); // int i=0, j=0; // QList::const_iterator it, it1; graphMatrixAdjacencyCreate(); QFile file( fn ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { emit statusMessage ( tr("Error. Could not write to ") + fn ); return; } QTextStream outText( &file ); outText.setCodec("UTF-8"); outText << AM.degreeMatrix(); file.close(); } /** * @brief Computes the Laplacian matrix of the graph and writes it to given file * @param fn */ void Graph::writeMatrixLaplacianPlainText(const QString &fn) { qDebug("Graph::writeMatrixLaplacianPlainText() "); // int i=0, j=0; // QList::const_iterator it, it1; graphMatrixAdjacencyCreate(); QFile file( fn ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) { emit statusMessage ( tr("Error. Could not write to ") + fn ); return; } QTextStream outText( &file ); outText.setCodec("UTF-8"); outText << AM.laplacianMatrix(); file.close(); } /** This method is automatically invoked when a QTimerEvent occurs UNUSED */ void Graph::timerEvent(QTimerEvent *event) { qDebug("Graph: timerEvent()"); Q_UNUSED(event); if (!graphModified()) { qDebug("Timer will be KILLED since no vertex is movin any more..."); killTimer(timerId); timerId = 0; } } /** * @brief Graph::layoutForceDirectedSpringEmbedder * @param maxIterations * The Spring Embedder model (Eades, 1984), part of the Force Directed Placement (FDP) family, assigns forces to all vertices and edges, as if nodes were electrically charged particles (Coulomb's law) and all edges were springs (i.e. Hooke's law). These forces are applied to the nodes iteratively, pulling them closer together or pushing them further apart, until the system comes to an equilibrium state (node positions do not change anymore). Note that, following Eades, we do not need to have a faithful simulation; we can -and we do- apply unrealistic forces in an unrealistic manner. For instance, instead of the forces described by Hooke's law, we will assume weaker logarithmic forces between far apart vertices... */ void Graph::layoutForceDirectedSpringEmbedder(const int maxIterations){ int progressCounter=0; qreal dist = 0; qreal f_rep=0, f_att=0; QPointF DV; qreal c4=0.1; //normalization factor for final displacement QList::const_iterator v1; QList::const_iterator v2; /* apply an inital random layout */ // layoutRandom(); /** * compute max spring length as function of canvas area divided by the * total vertices area */ qreal V = (qreal) vertices() ; qreal naturalLength= computeOptimalDistance(V); qDebug() << "\n\n layoutForceDirectedSpringEmbedder() " << " vertices " << V << " naturalLength " << naturalLength; int iteration = 1 ; for ( iteration=1; iteration <= maxIterations ; iteration++) { //setup init disp for (v1=m_graph.cbegin(); v1!=m_graph.cend(); ++v1) { (*v1) -> disp().rx() = 0; (*v1) -> disp().ry() = 0; qDebug() << " 0000 s " << (*v1)->name() << " zeroing rx/ry"; } for (v1=m_graph.cbegin(); v1!=m_graph.cend(); ++v1) { qDebug() << "********* Calculate forces for source s " << (*v1) -> name() <<" pos "<< (*v1) -> x()<< ", "<< (*v1) -> y(); if ( ! (*v1)->isEnabled() ) { qDebug() << " vertex s disabled. Continue"; continue; } for (v2=m_graph.cbegin(); v2!=m_graph.cend(); ++v2) { if ( ! (*v2)->isEnabled() ) { qDebug() << " t " << (*v1)->name() << " disabled. Continue"; continue; } if (v2 == v1) { qDebug() << " s==t, continuing"; continue; } DV.setX( (*v2) -> x() - (*v1)->x()); DV.setY( (*v2) -> y() - (*v1)->y()); dist = graphDistanceEuclidean(DV); /** * calculate electric (repulsive) forces between * all vertices. */ f_rep = layoutForceDirected_F_rep ("Eades", dist, naturalLength) ; (*v1)->disp().rx() += sign( DV.x() ) * f_rep ; (*v1)->disp().ry() += sign( DV.y() ) * f_rep ; qDebug() <<" s = "<< (*v1)->name() <<" pushed away from t = " << (*v2) -> name() << " dist " < naturalLength) * or push them apart (if d < naturalLength) */ if ( this->edgeExists( (*v1) ->name(), (*v2) -> name()) ) { f_att = layoutForceDirected_F_att ("Eades", dist, naturalLength) ; (*v1)->disp().rx() += sign( DV.x() ) * f_att ; (*v1)->disp().ry() += sign( DV.y() ) * f_att ; (*v2)->disp().rx() -= sign( DV.x() ) * f_att ; (*v2)->disp().ry() -= sign( DV.y() ) * f_att ; qDebug() << " s= "<<(*v1)->name() << " attracted by t= "<< (*v2)->name() << " dist " <>> final s = "<< (*v1)->name() << " disp_s.x="<< (*v1)->disp().rx() << " disp_s.y="<< (*v1)->disp().ry(); } // end for v1 layoutForceDirected_Eades_moveNodes(c4) ; progressCounter++; emit updateProgressDialog(progressCounter ); } //end iterations } /** * @brief Graph::layoutForceDirectedFruchtermanReingold * @param maxIterations * Fruchterman and Reingold (1991) refined the Spring Embedder model by replacing the forces. In this model, "the vertices behave as atomic particles or celestial bodies, exerting attractive and repulsive forces on one another." (ibid). Again, only vertices that are neighbours attract each other but, unlike Spring Embedder, all vertices repel each other. These forces induce movement. The algorithm might resemble molecular or planetary simulations, sometimes called n-body problems. */ void Graph::layoutForceDirectedFruchtermanReingold(const int maxIterations){ int progressCounter=0; qreal dist = 0; qreal f_att, f_rep; QPointF DV; //difference vector //qreal temperature=canvasWidth / 10.0; //limits the displacement of the vertex //qreal temperature=5.8309518948453; //limits the displacement of the vertex qreal V = (qreal) vertices() ; qreal C=0.9; //this is found experimentally // optimalDistance (or k) is the radius of the empty area around a vertex - // we add vertexWidth to it qreal optimalDistance= C * computeOptimalDistance(V); /* apply an inital random layout */ // layoutRandom(); qDebug() << "Graph: layoutForceDirectedFruchtermanReingold() "; qDebug () << "Graph: Setting optimalDistance = "<< optimalDistance << "...following Fruchterman-Reingold (1991) formula "; qDebug() << "Graph: canvasWidth " << canvasWidth << " canvasHeight " << canvasHeight; QList::const_iterator v1; QList::const_iterator v2; int iteration = 1 ; for ( iteration=1; iteration <= maxIterations ; iteration++) { //setup init disp for (v1=m_graph.cbegin(); v1!=m_graph.cend(); ++v1) { (*v1)->disp().rx() = 0; (*v1)->disp().ry() = 0; //qDebug() << " 0000 s " << (*v1)->name() << " zeroing rx/ry"; } for (v1=m_graph.cbegin(); v1!=m_graph.cend(); ++v1) { // qDebug() << "***** Calculate forces for s " << (*v1)->name() // << " index " << index[(*v1)->name()] // << " pos "<< (*v1)->x() << ", "<< (*v1)->y(); if ( ! (*v1)->isEnabled() ) { // qDebug() << " vertex s " << (*v1)->name() << " disabled. Continue"; continue; } for (v2=m_graph.cbegin(); v2!=m_graph.cend(); ++v2) { // qDebug () << " t = "<< (*v2)->name() // << " pos (" << (*v2)->x() << "," << (*v2)->y() << ")"; if ( ! (*v2)->isEnabled() ) { // qDebug()<< " t "<< (*v2)->name()<< " disabled. Continue"; continue; } if (v2 == v1) { // qDebug() << " s==t, continuing"; continue; } DV.setX( (*v2)->x() - (*v1)->x() ); DV.setY( (*v2)->y() - (*v1)->y() ); dist = graphDistanceEuclidean( DV ); //calculate repulsive force from _near_ vertices f_rep = layoutForceDirected_F_rep( "FR", dist, optimalDistance); (*v1)->disp().rx() += sign( DV.x() ) * f_rep; (*v1)->disp().ry() += sign( DV.y() ) * f_rep ; // qDebug()<< " dist( " << (*v1)->name() << "," << (*v2)->name() << " = " // << dist // << " f_rep " << f_rep // << " disp_s.x="<< (*v1)->disp().rx() // << " disp_s.y="<< (*v1)->disp().ry(); if ( edgeExists ((*v1)->name(), (*v2)->name()) ) { //calculate attracting force f_att = layoutForceDirected_F_att ("FR", dist, optimalDistance); (*v1)->disp().rx() += sign( DV.x() ) * f_att; (*v1)->disp().ry() += sign( DV.y() ) * f_att; (*v2)->disp().rx() -= sign( DV.x() ) * f_att ; (*v2)->disp().ry() -= sign( DV.y() ) * f_att ; qDebug() << " s= "<<(*v1)->name() << " attracted by t= "<< (*v2)->name() <<" optimalDistance =" << optimalDistance << " f_att " << f_att << " disp_s.x="<< (*v1)->disp().rx() << " disp_s.y="<< (*v1)->disp().ry() << " disp_t.x="<< (*v2)->disp().rx() << " disp_t.y="<< (*v2)->disp().ry(); } //endif }//end for v2 } //end for v1 // limit the max displacement to the temperature t // prevent placement outside of the frame/canvas layoutForceDirected_FR_moveNodes( layoutForceDirected_FR_temperature (iteration) ); progressCounter++; emit updateProgressDialog(progressCounter ); } } /** * @brief Graph::layoutForceDirectedKamadaKawai * In this model, the graph is considered to be a dynamic system * where every two vertices are connected by a 'spring'. Each spring * has a desirable length, which corresponds to their graph theoretic * distance. In this way, the optimal layout of the graph * is the state with the minimum imbalance. The degree of * imbalance is formulated as the total spring energy: * the square summation of the differences between desirable * distances and real ones for all pairs of vertices * @return qreal temperature */ void Graph::layoutForceDirectedKamadaKawai(const int maxIterations){ qDebug()<< "Graph::layoutForceDirectedKamadaKawai() - " << "maxIter " << maxIterations; Q_UNUSED(maxIterations); // Compute dij for 1 <= i!=j <= n bool considerWeights=false, inverseWeights=false, dropIsolates=false; graphMatrixDistancesCreate(false,considerWeights,inverseWeights, dropIsolates); qDebug() << " DM : "; //DM.printMatrixConsole(); // Compute lij for 1 <= i!=j <= n using the formula: // lij = L x dij // where L the desirable length of a single edge in the display pane // Since we are on a restricted display (a canvas), L depends on the // diameter D of the graph and the length L of a side of the display square: // L = L0 / D float L = canvasMinDimension() / (float) graphDiameter(considerWeights,inverseWeights); TM.zeroMatrix(DM.rows(), DM.cols()); TM=DM; TM.multiplyScalar(L); // TM.printMatrixConsole(); // Compute kij for 1 <= i!=j <= n using the formula: // ki // kij is the strength of the spring between pi and pj // initialize p1, p2, ... pn; // while ( max_D_i > e ) // let pm the particle satisfying D_m = max_D_i // while ( D_m > e) // compute delta_x and delta_y by solving equations 11 and 12 // x_m = x_m + delta_x // y_m = y_m + delta_y } /** * @brief Graph::layoutForceDirected_FR_temperature * Reduces the temperature as the layout approaches a better configuration * @return qreal temperature */ qreal Graph::layoutForceDirected_FR_temperature(const int iteration) const{ qreal temp=0; qDebug() << "Graph::layoutForceDirected_FR_temperature(): cool iteration " << iteration; if (iteration < 10) temp =canvasWidth / 10.0; else temp =canvasWidth / (iteration + 10) ; if (iteration > 100) // follow Eades advice... temp = 0; qDebug() << "Graph::layoutForceDirected_FR_temperature() - iteration " << iteration << " temp " << temp; return temp; } /** * @brief Graph::computeOptimalDistance * @return qreal optimalDistance */ qreal Graph::computeOptimalDistance(const int &Vertices){ qreal vertexWidth = (qreal) 2.0 * initVertexSize ; qreal screenArea = canvasHeight*canvasWidth; qreal vertexArea = qCeil ( qSqrt( screenArea / Vertices ) ) ; // optimalDistance (or k) is the radius of the empty area around a vertex - // we add vertexWidth to it return (vertexWidth + vertexArea); } qreal Graph::layoutForceDirected_F_att( const QString model, const qreal &dist, const qreal &optimalDistance) { qreal f_att; if (model == "Eades") { qreal c_spring=2; f_att = c_spring * log10 ( dist / optimalDistance ); } else { // model -> FR f_att= ( dist * dist ) / optimalDistance; } return f_att; } qreal Graph::layoutForceDirected_F_rep( const QString model, const qreal &dist, const qreal &optimalDistance) { qreal f_rep; if (model == "Eades") { if (dist !=0){ qreal c_rep= 1.0; f_rep = c_rep / (dist * dist); if ( dist > (2.0 * optimalDistance) ) { //neglect vertices outside circular area of radius 2 * optimalDistance f_rep=0; } } else { f_rep = optimalDistance ; //move away } } else { // model -> FR // To speed up our algorithm we use the grid-variant algorithm. if ( (2.0 * optimalDistance) < dist ) { //neglect vertices outside circular area of radius 2*optimalDistance f_rep=0; } else { // repelsive forces are computed only for vertices within a circular area // of radius 2*optimalDistance f_rep = (optimalDistance * optimalDistance / dist) ; } } return -f_rep; } /** * @brief Graph::sign * returns the sign of number D as integer (1 or -1) * @param D * @return */ int Graph::sign(const qreal &D) { if (D != 0 ) { return ( D / qAbs(D) ); } else { return 0; } } /** * @brief Graph::compute_angles * Computes the two angles of the orthogonal triangle shaped by two points * of difference vector DV and distance dist * A = 90 * B = angle1 * C = angle2 * * @param DV * @param dist * @param angle1 * @param angle2 * @param degrees1 * @param degrees2 */ void Graph::compute_angles(const QPointF &DV, const qreal &dist, qreal &angle1, qreal &angle2, qreal °rees1, qreal °rees2 ) { if ( dist >0 ) { angle1 = qAcos( qAbs(DV.x()) / dist ); // radians angle2 = (M_PI / 2.0) -angle1; // radians (pi/2 -a1) } else { angle1 =0; angle2 =0; } degrees1 = angle1 * 180.0 / M_PI; // convert to degrees degrees2 = angle2 * 180.0 / M_PI; // convert to degrees qDebug () << "angle1 " <::const_iterator v1; for (v1=m_graph.cbegin(); v1!=m_graph.cend(); ++v1) { // calculate new overall velocity vector xvel = c4 * (*v1)->disp().rx(); yvel = c4 * (*v1)->disp().ry(); qDebug() << " ##### source vertex " << (*v1)->name() << " xvel,yvel = ("<< xvel << ", "<< yvel << ")"; //fix Qt error a positive QPoint to the floor // when we ask for setNodePos to happen. if ( xvel < 1 && xvel > 0 ) xvel = 1 ; if ( yvel < 1 && yvel > 0 ) yvel = 1 ; //Move source node to new position according to overall velocity newPos = QPointF( (qreal) (*v1)->x() + xvel, (qreal) (*v1)->y() + yvel); qDebug() << " source vertex v1 " << (*v1)->name() << " current pos: (" << (*v1)->x() << " , " << (*v1)->y() << " Possible new pos (" << newPos.x() << " , " << newPos.y(); // check if new pos is out of usable screen and adjust newPos.rx() = canvasVisibleX(newPos.x()); newPos.ry() = canvasVisibleY(newPos.y()); qDebug() << " Final new pos (" << newPos.x() << "," << newPos.y()<< ")"; (*v1)->setX( newPos.x() ); (*v1)->setY( newPos.y() ); emit setNodePos((*v1)->name(), newPos.x(), newPos.y()); //vertexPosSet(); } } /** * @brief Graph::layoutForceDirected_FR_moveNodes * @param temperature */ void Graph::layoutForceDirected_FR_moveNodes(const qreal &temperature) { qDebug() << "\n\n ***** layoutForceDirected_FR_moveNodes() \n\n " ; qDebug () << " temperature " << temperature; QPointF newPos; qreal xvel = 0, yvel = 0; QList::const_iterator v1; for (v1=m_graph.cbegin(); v1!=m_graph.cend(); ++v1) { // compute the new position // limit the maximum displacement to a maximum temperature xvel = sign((*v1)->disp().rx()) * qMin( qAbs((*v1)->disp().rx()), temperature) ; yvel = sign((*v1)->disp().ry()) * qMin( qAbs((*v1)->disp().ry()), temperature) ; newPos = QPointF((*v1)->x()+ xvel, (*v1)->y()+yvel); qDebug()<< " source vertex v1 " << (*v1)->name() << " current pos: (" << (*v1)->x() << "," << (*v1)->y() << ")" << "Possible new pos (" << newPos.x() << "," << newPos.y()<< ")"; newPos.rx() = canvasVisibleX (newPos.x()); // qMin ( // canvasWidth - 50.0 , qMax (50.0 , newPos.x() ) // ); newPos.ry() = canvasVisibleY (newPos.y()); // qMin ( // canvasHeight -50.0 , qMax (50.0 , newPos.y() ) // ); //Move node to new position qDebug()<< " final new pos " << newPos.x() << "," << newPos.y()<< ")"; (*v1)->setX( newPos.x() ); (*v1)->setY( newPos.y() ); emit setNodePos((*v1)->name(), newPos.x(), newPos.y()); } } /** * @brief Helper method, return the human readable name of matrix type. * @param matrix */ QString Graph::graphMatrixTypeToString(const int &matrixType) const { QString matrixStr; switch (matrixType) { case MATRIX_ADJACENCY : matrixStr = "Adjacency Matrix" ; break; case MATRIX_DISTANCES: matrixStr = "Distances Matrix" ; break; case MATRIX_DEGREE: matrixStr = "Degree Matrix" ; break; case MATRIX_LAPLACIAN: matrixStr = "Laplacian Matrix" ; break; case MATRIX_ADJACENCY_INVERSE: matrixStr = "Adjacency Inverse" ; break; case MATRIX_GEODESICS: matrixStr = "Geodesics Matrix" ; break; case MATRIX_REACHABILITY: matrixStr = "Reachability Matrix" ; break; case MATRIX_ADJACENCY_TRANSPOSE: matrixStr = "Adjacency Transpose" ; break; case MATRIX_COCITATION: matrixStr = "Cocitation Matrix" ; break; case MATRIX_DISTANCES_EUCLIDEAN : matrixStr = "Euclidean distance matrix"; break; case MATRIX_DISTANCES_MANHATTAN: matrixStr = "Manhattan distance matrix"; break; case MATRIX_DISTANCES_JACCARD: matrixStr = "Jaccard distance matrix"; break; case MATRIX_DISTANCES_HAMMING: matrixStr = "Hamming distance matrix"; break; default: matrixStr = "-" ; break; } return matrixStr; } /** * @brief Helper method, return the matrix type of human readable matrix name . * @param matrix * @return */ int Graph::graphMatrixStrToType(const QString &matrix) const { if (matrix.contains("Hamming", Qt::CaseInsensitive)) { return MATRIX_DISTANCES_HAMMING; } else if (matrix.contains("Jaccard", Qt::CaseInsensitive)) { return MATRIX_DISTANCES_JACCARD; } else if (matrix.contains("Manhattan", Qt::CaseInsensitive)) { return MATRIX_DISTANCES_MANHATTAN; } else if (matrix.contains("Euclidean", Qt::CaseInsensitive)) { return MATRIX_DISTANCES_EUCLIDEAN; } else if (matrix.contains("Cocitation", Qt::CaseInsensitive)) { return MATRIX_COCITATION; } else if (matrix.contains("Adjacency Transpose", Qt::CaseInsensitive)) { return MATRIX_ADJACENCY_TRANSPOSE; } else if (matrix.contains("Reachability", Qt::CaseInsensitive)) { return MATRIX_REACHABILITY; } else if (matrix.contains("Geodesics", Qt::CaseInsensitive)) { return MATRIX_GEODESICS; } else if (matrix.contains("Adjacency Inverse", Qt::CaseInsensitive)) { return MATRIX_ADJACENCY_INVERSE; } else if (matrix.contains("Laplacian", Qt::CaseInsensitive)) { return MATRIX_LAPLACIAN; } else if (matrix.contains("Degree", Qt::CaseInsensitive)) { return MATRIX_DEGREE; } else if (matrix.contains("Adjacency", Qt::CaseInsensitive)) { return MATRIX_ADJACENCY; } else if (matrix.contains("Distances", Qt::CaseInsensitive)) { return MATRIX_DISTANCES; } else { return -1; } } /** * @brief Helper method, return the human readable name of metric type. * @param metric */ QString Graph::graphMetricTypeToString(const int &metricType) const { QString metricStr; switch (metricType) { case METRIC_SIMPLE_MATCHING : metricStr = "Simple / Exact matching" ; break; case METRIC_JACCARD_INDEX: metricStr = "Jaccard Index" ; break; case METRIC_HAMMING_DISTANCE: metricStr = "Hamming distance" ; break; case METRIC_COSINE_SIMILARITY: metricStr = "Cosine similarity" ; break; case METRIC_EUCLIDEAN_DISTANCE: metricStr = "Euclidean distance" ; break; case METRIC_MANHATTAN_DISTANCE: metricStr = "Manhattan distance" ; break; case METRIC_PEARSON_COEFFICIENT: metricStr = "Pearson Correlation Coefficient" ; break; case METRIC_CHEBYSHEV_MAXIMUM: metricStr = "Chebyshev distance" ; break; default: metricStr = "-" ; break; } return metricStr; } /** * @brief Helper method, return the identifier of a metric. * @param metricStr */ int Graph::graphMetricStrToType(const QString &metricStr) const { int metric=METRIC_SIMPLE_MATCHING; if (metricStr.contains("Simple",Qt::CaseInsensitive)) metric = METRIC_SIMPLE_MATCHING ; else if (metricStr.contains("Jaccard",Qt::CaseInsensitive)) metric =METRIC_JACCARD_INDEX ; else if (metricStr.contains("None",Qt::CaseInsensitive)) metric =METRIC_NONE; else if (metricStr.contains("Hamming",Qt::CaseInsensitive)) metric =METRIC_HAMMING_DISTANCE; else if (metricStr.contains("Cosine",Qt::CaseInsensitive)) metric =METRIC_COSINE_SIMILARITY; else if (metricStr.contains("Euclidean",Qt::CaseInsensitive)) metric =METRIC_EUCLIDEAN_DISTANCE; else if (metricStr.contains("Manhattan",Qt::CaseInsensitive)) metric =METRIC_MANHATTAN_DISTANCE; else if (metricStr.contains("Pearson ",Qt::CaseInsensitive)) metric = METRIC_PEARSON_COEFFICIENT; else if (metricStr.contains("Chebyshev",Qt::CaseInsensitive)) metric = METRIC_CHEBYSHEV_MAXIMUM; return metric; } /** * @brief Helper method, return the human readable name of clustering method type. * @return */ QString Graph::graphClusteringMethodTypeToString(const int &methodType) const { QString methodStr; switch (methodType) { case CLUSTERING_SINGLE_LINKAGE: methodStr = "Single-linkage (minumum)"; break; case CLUSTERING_COMPLETE_LINKAGE: methodStr = "Complete-linkage (maximum)"; break; case CLUSTERING_AVERAGE_LINKAGE: methodStr = "Average-linkage (UPGMA)"; break; default: break; } return methodStr; } /** * @brief Helper method, return clustering method type from the human readable name of it. * @param method * @return */ int Graph::graphClusteringMethodStrToType(const QString &method) const { int methodType=CLUSTERING_AVERAGE_LINKAGE; if (method.contains("Single", Qt::CaseInsensitive)) { methodType = CLUSTERING_SINGLE_LINKAGE; } else if (method.contains("Complete", Qt::CaseInsensitive)) { methodType = CLUSTERING_COMPLETE_LINKAGE; } else if (method.contains("Average", Qt::CaseInsensitive)) { methodType = CLUSTERING_AVERAGE_LINKAGE; } return methodType; } /** * @brief Helper method, returns a nice qstring where all html special chars are encoded * @param str * @return */ QString Graph::htmlEscaped(QString str) const { str=str.simplified(); if (str.contains('&') ){ str=str.replace('&',"&"); } if (str.contains('<') ){ str=str.replace('<',"<"); } if (str.contains('>') ){ str=str.replace('>',">"); } if (str.contains('\"') ){ str=str.replace('\"',"""); } if (str.contains('\'') ){ str=str.replace('\'',"'"); } return str; } /** * @brief Graph::~Graph */ Graph::~Graph() { clear(); } socnetv-2.2/src/graph.h000755 000765 000024 00000125213 13040734202 016070 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt graph.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef GRAPH_H #define GRAPH_H #include #include // used in exporting centrality files #include #include #include #include //FYI: stack is a wrapper around in C++, see: www.cplusplus.com/reference/stl/stack #include #include #include "vertex.h" #include "matrix.h" #include "parser.h" #include "webcrawler.h" using namespace std; static const int EDGE_DIRECTED = 0; static const int EDGE_DIRECTED_OPPOSITE_EXISTS = 1; static const int EDGE_RECIPROCAL_UNDIRECTED = 2; static const int FILE_GRAPHML = 1; // .GRAPHML .XML static const int FILE_PAJEK = 2; // .PAJ .NET static const int FILE_ADJACENCY = 3; // .ADJ .CSV .SM static const int FILE_GRAPHVIZ = 4; // .DOT static const int FILE_UCINET = 5; // .DL .DAT static const int FILE_GML = 6; // .GML static const int FILE_EDGELIST_WEIGHTED = 7; // .CSV, .TXT, .LIST, LST, WLST static const int FILE_EDGELIST_SIMPLE = 8; // .CSV, .TXT, .LIST, LST static const int FILE_TWOMODE = 9; // .2SM .AFF static const int FILE_UNRECOGNIZED =-1; // UNRECOGNIZED FILE FORMAT static const int GRAPH_CHANGED_NONE = 0; static const int GRAPH_CHANGED_MINOR_OPTIONS = 1; static const int GRAPH_CHANGED_VERTICES_METADATA = 2; static const int GRAPH_CHANGED_EDGES_METADATA = 3; static const int GRAPH_CHANGED_POSITIONS = 4; static const int GRAPH_CHANGED_VERTICES = 11; static const int GRAPH_CHANGED_EDGES = 12; static const int GRAPH_CHANGED_VERTICES_AND_EDGES = 13; static const int GRAPH_CHANGED_NEW = 14; static const int CLUSTERING_SINGLE_LINKAGE = 0; //"single-link" or minimum static const int CLUSTERING_COMPLETE_LINKAGE = 1; // "complete-link or maximum static const int CLUSTERING_AVERAGE_LINKAGE = 2; static const int SUBGRAPH_CLIQUE = 1; static const int SUBGRAPH_STAR = 2; static const int SUBGRAPH_CYCLE = 3; static const int SUBGRAPH_LINE = 4; static const int MATRIX_ADJACENCY = 1; static const int MATRIX_DISTANCES = 2; static const int MATRIX_DEGREE = 3; static const int MATRIX_LAPLACIAN = 4; static const int MATRIX_ADJACENCY_INVERSE = 5; static const int MATRIX_GEODESICS = 6; static const int MATRIX_REACHABILITY = 7; static const int MATRIX_ADJACENCY_TRANSPOSE = 8; static const int MATRIX_COCITATION = 9; static const int MATRIX_DISTANCES_EUCLIDEAN = 12; static const int MATRIX_DISTANCES_MANHATTAN= 13; static const int MATRIX_DISTANCES_JACCARD= 14; static const int MATRIX_DISTANCES_HAMMING= 15; static const int MATRIX_DISTANCES_CHEBYSHEV= 16; class QPointF; typedef QList Vertices; typedef QHash H_StrToInt; typedef QHash H_Int; typedef QPair pair_f_b; typedef QPair rel_w_bool; typedef QHash < int, rel_w_bool > H_edges; typedef QHash H_StrToBool; typedef QList L_int; typedef QVector V_int; typedef QVector V_str; struct ClickedEdge { int v1; int v2; }; typedef pair SelectedEdge; class GraphDistance { public: int target; int distance; GraphDistance(int t, int dist) : target(t), distance(dist) { } }; // implement a min-priority queue class GraphDistancesCompare { public: bool operator()(GraphDistance& t1, GraphDistance& t2) { if (t1.distance == t2.distance) return t1.target > t2.target; return t1.distance > t2.distance; //minimum priority // Returns true if t1 is closer than t2 // else } }; /** TODO & KNOWN BUGS: \todo Group edge editing: i.e. change weight or color. \todo Enrich Node properties dialog \todo Update app icons \todo - CHECK weighted networks results (IRCC and distance matrix with other combinations) \todo - CHECK graphWeighted corner case results, when !graphModified. \todo - CHECK graphConnectedness() algorithm implementation (m_vertexPairsUnilaterallyConnected) \bug Create d-regular, undirected, ask for closeness, it says we are on a disconnected graph \bug Crash on Graphml files with textlabels instead of nodenumbers (i.e. nets/killer.graphml) \bug wontfix Crash on Graphml files with html special chars in node/edge labels \bug Pajek files with no ic / label in nodes are displayed without colors??? \bug wrong default edge colors (not the ones used by Settings) after loading GraphML files. \bug Resizing the MW view does not resize/reposition the layout guides \bug Fruchterman-Reingold model fixes some nodes to (1,1) breaking the layout TODO Change Menus to: Matrices Cohesion/Connectedness: Density, Reachability, and All distance and Walks, Connectivity, Reciprocity, Transitivity ?, Clu Cof Prominence: Centrality and Prestige Subgroups / Communities: Cliques (later clans etc), (later path distance MDS) Components, Blocks and Cutpoints, Structural Equivalence: HCA, Similarity (later MDS), Block modelling, CONCOR */ /** * @brief The Graph class * This is the main class for a Graph, used in conjuction with Vertex, Parser and Matrix objects. * Graph class methods are the interface to various analysis algorithms * Vertex class holds each vertex data (colors, strings, statistics, etc) * Matrix class holds the adjacency matrix of the network. * Parser class loads files of networks. */ class Graph: public QObject{ Q_OBJECT QThread file_parserThread; QThread wc_parserThread; QThread wc_spiderThread; public slots: int relationCurrent(); QString relationCurrentName() const; void relationCurrentRename(const QString &newName, const bool ¬ifyMW=false); /** Slots to signals from Parser */ void vertexCreate(const int &num, const int &size, const QString &nodeColor, const QString &numColor, const int &numSize, const QString &label, const QString &lColor, const int &labelSize, const QPointF &p, const QString &nodeShape, const bool &signalMW );//Main vertex creation call void graphFileLoaded(const int &fileType, const QString &fName=QString::null, const QString &netName=QString::null, const int &totalNodes=0, const int &totalLinks=0, const bool &undirected=false, const QString &message=QString::null); void vertexRemoveDummyNode(int); void graphLoadedTerminateParserThreads (QString reason); /** Slots to signals from GraphicsWidget and Parser*/ void edgeCreate (const int &v1, const int &v2, const float &weight, const QString &color , const int &type=0, const bool &drawArrows=true, const bool &bezier=false, const QString &label=QString::null, const bool &signalMW=true); void edgeCreateWebCrawler (const int &source, const int &target); void edgeVisibilitySet(int relation, int, int, bool); //auxiliary vertexCreate functions void vertexCreateAtPos(const QPointF &p); void vertexCreateAtPosRandom(const bool &signalMW=false); void vertexCreateAtPosRandomWithLabel(const int &i, const QString &label, const bool &signalMW=false) ; /** Slots to signals from MainWindow */ void relationSet(int index=RAND_MAX, const bool notifyMW=true); void relationNext(); void relationPrev(); void canvasSizeSet(const int w, const int h); double canvasMaxRadius() const; float canvasMinDimension() const; double canvasVisibleX(const double &x) const ; double canvasVisibleY(const double &y) const ; double canvasRandomX() const; double canvasRandomY() const; void vertexIsolateFilter ( bool ); //Called by MW to filter orphan vertices void vertexClickedSet(const int &v); void edgeClickedSet(const int &v1, const int &v2) ; void edgeFilterByWeight (float, bool); //Called by MW to filter edges over/under a weight void edgeFilterByRelation(int relation, bool status); void edgeFilterUnilateral(const bool &toggle); void webCrawl(QString, int, int, bool extLinks, bool intLinks); //Called by MW to start a web crawler... QString htmlEscaped (QString str) const; signals: /** Signals to MainWindow */ void updateProgressDialog(int ); void signalGraphModified(const int &graphStatus, const bool &undirected, const int &vertices, const int &edges, const float &density); void signalGraphLoaded (const int &fileType, const QString &fileName=QString::null, const QString &netName=QString::null, const int &totalNodes=0, const int &totalLinks=0, const QString &message=QString::null ); void signalGraphSaved(const int &status); void statusMessage (const QString &message); void signalDatasetDescription(QString); void signalNodeSizesByOutDegree(bool); void signalNodeSizesByInDegree(bool); void signalNodeClickedInfo(const int &number=0, const QPointF &p=QPointF(), const QString &label=QString::null, const int &inDegree=0, const int &outDegree=0, const float &clc=0); void signalEdgeClickedInfo (const long int &v1=0, const long int &v2=0, const float &weight=0, const bool &undirected=false); void signalRelationAddToMW(const QString &newRelation, const bool &changeRelation=true); void signalRelationsClear(); void signalRelationRenamedToMW(const QString newRelName); void signalRelationChangedToGW(int); void signalRelationChangedToMW(const int &relIndex=RAND_MAX); void signalSelectionChanged(const int &selectedVertices, const int &selectedEdges); /** Signals to GraphicsWidget */ void drawNode( const int &num, const int &size, const QString &nodeShape, const QString &nodeColor, const bool &showNumbers,const bool &numbersInside, const QString &numberColor, const int &numSize, const int &numDistance, const bool &showLabels, const QString &label, const QString &labelColor, const int &labelSize, const int &labelDistance, const QPointF &p ); void eraseNode (long int); //erase node from GW //call GW to draw an edge void drawEdge ( const int &v1, const int &v2, const float &weight, const QString &label="", const QString &color="black", const int &type=0, const bool arrows=true, const bool &bezier=false, const bool &weightNumbers=false); void eraseEdge(const long int &, const long int &); //emited from edgeRemove() to GW to clear the edge item. void setEdgeVisibility (int, int, int, bool); // emitted from each Vertex void setVertexVisibility(long int, bool); //notifies GW to disable a node void setNodePos(const int &, const qreal &, const qreal &); void setNodeSize(const long int &v, const int &size); void setNodeShape(const long int v, const QString &shape); void setNodeColor(const long int v, const QString &color); void setNodeLabel(long int, QString); void setNodeNumberSize(const long int &, const int &); void setNodeNumberDistance(const long int &, const int &); void setNodeLabelSize(const long int &, const int &); void setNodeLabelColor(const long int &, const QString &color); void setNodeLabelDistance(const long int &, const int &); void setEdgeWeight (const long int &v1, const long int &v2, const float &weight); void setEdgeUndirected(const long int &v1, const long int &v2, const float &weight); void setEdgeColor(const long int &v1, const long int &v2, const QString &color); void setEdgeLabel (const long int &v1, const long int &v2, const QString &label); void addGuideCircle(const double&, const double&, const double&); void addGuideHLine (const double&y0); /** Signals to Crawler threads */ void operateSpider(); public: /* INIT AND CLEAR*/ Graph(); void clear(); ~Graph(); //destroy object void setSocNetV_Version (QString ver) { VERSION = ver; } /*FILES (READ AND WRITE)*/ QString graphName() const; void graphLoad (const QString m_fileName, const QString m_codecName, const bool m_showLabels, const int format, const int two_sm_mode, const QString delimiter=QString::null); void graphSave(const QString &fileName, const int &fileType, const bool &saveEdgeWeights=true); bool graphSaveToPajekFormat (const QString &fileName, QString networkName="", int maxWidth=0, int maxHeight=0); bool graphSaveToAdjacencyFormat (const QString &fileName, const bool &saveEdgeWeights=true); bool graphSaveToGraphMLFormat (const QString &fileName, QString networkName="", int maxWidth=0, int maxHeight=0); bool graphSaveToDotFormat (QString fileName); int graphFileFormat() const; bool graphFileFormatExportSupported(const int &fileFormat) const; QString graphMatrixTypeToString(const int &matrixType) const; int graphMatrixStrToType(const QString &matrix) const; QString graphMetricTypeToString(const int &metricType) const; int graphMetricStrToType(const QString &metricStr) const; QString graphClusteringMethodTypeToString(const int &methodType) const; int graphClusteringMethodStrToType(const QString &method) const; /* RELATIONS */ int relations(); void relationsClear(); void relationAdd(const QString &relName, const bool &changeRelation=false); /* VERTICES */ int vertexNumberMax(); int vertexNumberMin(); int vertexDegreeOut(int); int vertexDegreeIn(int); QList vertexNeighborhoodList(const int &v1); bool vertexIsolated(const long int &v1) const; int vertexExists(const long int &v1 ); int vertexExists(const QString &label); void vertexRemove (long int ); void vertexSizeInit (const long int); void vertexSizeSet(const long int &v, const int &newsize ); void vertexSizeAllSet(const int newsize); int vertexSize(const long int &v); void vertexShapeInit (const QString); void vertexShapeSet(const int v, const QString shape); void vertexShapeAllSet(const QString shape); QString vertexShape(const int &v); void vertexColorInit (const QString &color); void vertexColorSet(const long &v, const QString &color); void vertexColorAllSet(const QString &color); QColor vertexColor(const long int &v); void vertexNumberColorInit ( QString color); void vertexNumberSizeInit (const int &size); void vertexNumberSizeSet(const long int &v, const int &newsize ); void vertexNumberSizeSetAll (const int &size); void vertexNumberDistanceInit (const int &distance); void vertexNumberDistanceSet(const long int &v, const int &newDistance ); void vertexNumberDistanceSetAll (const int &newDistance); void vertexNumbersInsideNodesSet(bool toggle); void vertexNumbersVisibilitySet(bool toggle); void vertexLabelsVisibilitySet(bool toggle); void vertexLabelSizeInit(int newSize); void vertexLabelSizeSet(const long int &v, const int &newsize ); void vertexLabelSizeAllSet (const int &); void vertexLabelColorInit(QString color); void vertexLabelSet(int v, QString label); void vertexLabelColorSet(int v1, QString color); void vertexLabelColorAllSet(const QString &color); QString vertexLabel(const long int &v1); void vertexLabelDistanceInit (const int &distance); void vertexLabelDistanceSet(const long int &v, const int &newDistance ); void vertexLabelDistanceAllSet (const int &newDistance); void vertexPosSet(const int &v, const int &x, const int &y); QPointF vertexPos(const int &v1); int vertexClicked() const; int vertices(const bool dropIsolates=false, const bool countAll=false) ; int vertexEdgesOutbound (int i) ; int vertexEdgesInbound (int i) ; int verticesWithOutboundEdges(); int verticesWithInboundEdges(); int verticesWithReciprocalEdges(); QList verticesListIsolated(); QList verticesList(); QSet verticesSet(); void verticesCreateSubgraph(QList vList, const int &type = SUBGRAPH_CLIQUE, const int ¢er = 0); qreal graphDistanceEuclidean(const QPointF &a, const QPointF &b); qreal graphDistanceEuclidean(const QPointF &a); int sign(const qreal &D); qreal layoutForceDirected_F_rep(const QString model, const qreal &dist, const qreal &optimalDistance) ; qreal layoutForceDirected_F_att(const QString model, const qreal &dist, const qreal &optimalDistance) ; void layoutForceDirected_Eades_moveNodes(const qreal &c4); void layoutForceDirected_FR_moveNodes(const qreal &temperature) ; qreal layoutForceDirected_FR_temperature(const int iteration) const; qreal computeOptimalDistance(const int &Vertices); void compute_angles( const QPointF &Delta, const qreal &dist, qreal &angle1, qreal &angle2, qreal °rees1, qreal °rees2 ); /* EDGES */ int edgesEnabled(); ClickedEdge edgeClicked(); float edgeExists(const long &v1, const long &v2, const bool &undirected=false); void edgeRemove (const long int &v1, const long int &v2, const bool &removeOpposite=false); bool edgeSymmetric(const long &v1, const long &v2); void edgeUndirectedSet(const long int &v1, const long int &v2, const float &w); void edgeWeightSet (const long int &v1, const long int &v2, const float &w, const bool &undirected=false); float edgeWeight(const long int &v1, const long int &v2) const; void edgeWeightNumbersVisibilitySet (const bool &toggle); void edgeLabelSet(const long int &v1, const long int &v2, const QString &label); QString edgeLabel (const long int &v1, const long int &v2) const; void edgeLabelsVisibilitySet (const bool &toggle); void edgeColorInit(const QString &); void edgeColorSet(const long int &v1, const long int &v2, const QString &color); QString edgeColor (const long int &v1, const long int &v2); bool edgeColorAllSet(const QString &color, const int &threshold=RAND_MAX); //GRAPH methods void graphModifiedSet(const int &graphNewStatus, const bool&signalMW=true); bool graphModified() const ; bool graphSaved() const; bool graphLoaded() const; void graphSelectionChanged(const QList &selectedVertices, const QList &selectedEdges); QList graphSelectedVertices() const; int graphSelectedVerticesCount() const; int graphSelectedVerticesMin() const; int graphSelectedVerticesMax() const; QList graphSelectedEdges() const; int graphSelectedEdgesCount() const; int graphGeodesics(); float graphDensity(); bool graphWeighted(); bool graphSymmetric(); void graphSymmetrize(); void graphSymmetrizeStrongTies(const bool &allRelations=false); void graphCocitation(); void graphUndirectedSet(const bool &toggle, const bool &signalMW=true); bool graphUndirected(); void graphMatrixAdjacencyCreate(const bool dropIsolates=false, const bool considerWeights=true, const bool inverseWeights=false, const bool symmetrize=false ); bool graphMatrixAdjacencyInvert(const QString &method="lu"); void graphMatrixSimilarityMatchingCreate(Matrix &AM, Matrix &SEM, const int &measure=METRIC_SIMPLE_MATCHING, const QString &varLocation="Rows", const bool &diagonal=false, const bool &considerWeights=true); void graphMatrixSimilarityPearsonCreate (Matrix &AM, Matrix &PCC, const QString &varLocation="Rows", const bool &diagonal=false); void graphMatrixDissimilaritiesCreate(Matrix &INPUT_MATRIX, Matrix &DSM, const int &metric, const QString &varLocation, const bool &diagonal, const bool &considerWeights); /* REPORT EXPORTS */ void writeDataSetToFile(const QString dir, const QString ); void writeMatrixAdjacencyTo(QTextStream& os, const bool &saveEdgeWeights=true); void writeMatrix(const QString &fileName, const int &matrix=MATRIX_ADJACENCY, const bool &considerWeights=true, const bool &inverseWeights=false, const bool &dropIsolates=false, const QString &varLocation="Rows", const bool &simpler=false); void writeMatrixAdjacency(const QString fileName, const bool &markDiag=true); void writeMatrixAdjacencyPlot(const QString fileName, const bool &simpler=false); void writeMatrixAdjacencyInvert(const QString &filename, const QString &method); void writeMatrixLaplacianPlainText(const QString &filename); void writeMatrixDegreeText(const QString &filename); void writeMatrixDistancesPlainText(const QString &fn, const bool &considerWeights, const bool &inverseWeights, const bool &dropIsolates); void writeMatrixNumberOfGeodesicsPlainText(const QString &fn, const bool &considerWeights, const bool &inverseWeights); void writeMatrixDissimilarities(const QString fileName, const QString &metricStr, const QString &varLocation, const bool &diagonal, const bool &considerWeights) ; void writeMatrixSimilarityMatchingPlain(const QString fileName, const int &measure=METRIC_SIMPLE_MATCHING, const QString &matrix = "adjacency", const QString &varLocation="rows", const bool &diagonal=false, const bool &considerWeights=true); void writeMatrixSimilarityMatching(const QString fileName, const QString &measure="Simple", const QString &matrix = "adjacency", const QString &varLocation="rows", const bool &diagonal=false, const bool &considerWeights=true); void writeMatrixSimilarityPearson(const QString fileName, const bool considerWeights, const QString &matrix = "adjacency", const QString &varLocation="rows", const bool &diagonal=false); void writeMatrixSimilarityPearsonPlainText(const QString fileName, const bool considerWeights, const QString &matrix = "adjacency", const QString &varLocation="rows", const bool &diagonal=false); void writeEccentricity(const QString, const bool considerWeights, const bool inverseWeights, const bool dropIsolates); // friend QTextStream& operator << (QTextStream& os, Graph& m); void writeCentralityDegree(const QString, const bool weights, const bool dropIsolates); void writeCentralityCloseness(const QString, const bool weights, const bool inverseWeights, const bool dropIsolates); void writeCentralityClosenessInfluenceRange(const QString, const bool weights, const bool inverseWeights, const bool dropIsolates); void writeCentralityBetweenness(const QString, const bool weights, const bool inverseWeights, const bool dropIsolates); void writeCentralityPower(const QString, const bool weigths, const bool inverseWeights, const bool dropIsolates); void writeCentralityStress(const QString, const bool weigths, const bool inverseWeights, const bool dropIsolates); void writeCentralityEccentricity(const QString, const bool weigths, const bool inverseWeights, const bool dropIsolates); void writeCentralityInformation(const QString, const bool weigths, const bool inverseWeights); void writeCentralityEigenvector(const QString, const bool &weigths=true, const bool &inverseWeights = false, const bool &dropIsolates=false); void writePrestigeDegree(const QString, const bool weights, const bool dropIsolates); void writePrestigeProximity(const QString, const bool weights, const bool inverseWeights, const bool dropIsolates); void writePrestigePageRank(const QString, const bool Isolates=false); void writeClusteringHierarchical(const QString &fileName, const QString &matrix = "Adjancency", const QString &metric = "Manhattan", const QString &method = "Complete", const bool &diagonal = false, const bool &dendrogram = false, const bool &considerWeights=true, const bool &inverseWeights=false, const bool &dropIsolates=false); void writeClusteringHierarchicalResultsToStream(QTextStream& outText, const int N, const bool &dendrogram = false); void writeCliqueCensus( const QString fileName, const bool considerWeights ); void writeClusteringCoefficient(const QString, const bool); void writeTriadCensus(const QString, const bool); /* DISTANCES, CENTRALITIES & PROMINENCE MEASURES */ int graphDistanceGeodesic(const int, const int, const bool considerWeights, const bool inverseWeights); int graphDiameter(const bool considerWeights, const bool inverseWeights); float graphDistanceGeodesicAverage(const bool considerWeights, const bool inverseWeights, const bool dropIsolates); int graphConnectedness(const bool updateProgress=false) ; void graphMatrixDistancesCreate(const bool &computeCentralities=false, const bool &considerWeights=false, const bool &inverseWeights=true, const bool &dropIsolates=false); void centralityDegree(const bool &weights=true, const bool &dropIsolates=false); void centralityInformation(const bool considerWeights=false, const bool inverseWeights=false); void centralityEigenvector(const bool &considerWeights=false, const bool &inverseWeights=false, const bool &dropIsolates=false); void centralityClosenessIR(const bool considerWeights=false, const bool inverseWeights=false, const bool dropIsolates=false); void prestigeDegree(const bool &weights, const bool &dropIsolates=false); void prestigePageRank(const bool &dropIsolates=false); void prestigeProximity(const bool considerWeights=false, const bool inverseWeights=false, const bool dropIsolates=false); /* REACHABILTY AND WALKS */ int walksBetween(int v1, int v2,int length); void graphWalksMatrixCreate(const int &vertices=0, const int &length=0, const bool &updateProgress=false); void writeWalksTotalMatrixPlainText(const QString &fn); void writeWalksOfLengthMatrixPlainText(const QString &fn, const int &length); void writeMatrixWalks (const QString &fn, const int &length=0, const bool &simpler=false); int reachable(const int &v1, const int &v2) ; QList vertexinfluenceRange(int v1); QList vertexinfluenceDomain(int v2); void writeReachabilityMatrixPlainText(const QString &fn, const bool &dropIsolates=false); float numberOfTriples(int v1); /* CLIQUES, CLUSTERING, TRIADS */ void graphCliques(QSet R=QSet(), QSet P=QSet(), QSet X=QSet() ); void graphCliqueAdd (const QList &clique); int graphCliquesContaining(const int &actor, const int &size=0); int graphCliquesOfSize(const int &size ); void graphClusteringHierarchical(Matrix &STR_EQUIV, const int &metric, const int &method, const bool &diagonal=false, const bool &diagram=false, const bool &considerWeights=true, const bool &inverseWeights=false, const bool &dropIsolates=false); float clusteringCoefficientLocal(const long int &v1); float clusteringCoefficient (const bool updateProgress=false); bool graphTriadCensus(); void triadType_examine_MAN_label(int, int, int, Vertex*, Vertex*, Vertex* ); // void eccentr_JordanCenter(); // TODO /* LAYOUTS */ void layoutRandom(); void layoutCircularRandom(double x0, double y0, double maxRadius); void layoutCircularByProminenceIndex(double x0, double y0, double maxRadius, int type, const bool considerWeights, const bool inverseWeights, const bool dropIsolates); void layoutLevelByProminenceIndex(double maxWidth, double maxHeight, int type, const bool considerWeights, const bool inverseWeights, const bool dropIsolates); void layoutVerticesSizeByProminenceIndex(int index, const bool considerWeights, const bool inverseWeights, const bool dropIsolates); void layoutForceDirectedSpringEmbedder(const int maxIterations); void layoutForceDirectedFruchtermanReingold(const int maxIterations); void layoutForceDirectedKamadaKawai(const int maxIterations); /* CRAWLER */ void webCrawlTerminateThreads (QString reason); /**RANDOM NETWORKS*/ void randomizeThings(); void randomNetErdosCreate ( const int &vert, const QString &model, const int &edges, const float &eprob, const QString &mode, const bool &diag); void randomNetRingLatticeCreate (const int &vert, const int °ree, const bool updateProgress=false); void randomNetRegularCreate (const int &vert, const int °ree, const QString &mode, const bool &diag); void randomNetScaleFreeCreate (const int &n, const int &power, const int &m0, const int &m, const float &alpha, const QString &mode); void randomNetSmallWorldCreate(const int &vert, const int °ree, const double &beta, const QString &mode); int factorial (int); /** index stores the real position of each vertex inside m_graph. * It starts at zero (0). * We need to know the place of a vertex inside m_graph after adding * or removing many vertices */ H_Int index; // Stores the number of vertices at distance n from a given vertex H_Int sizeOfNthOrderNeighborhood; /* maps have O(logN) lookup complexity */ /* Consider using tr1::hashmap which has O(1) lookup, but this is not ISO C++ yet :( */ protected: // Called from nodeMovement when a timerEvent occurs void timerEvent(QTimerEvent *event); private: /** * List of pointers to the vertices. * A vertex stores all the info: links, colours, etc */ Vertices m_graph; Parser *file_parser; //file loader threaded class. WebCrawler_Parser *wc_parser; WebCrawler_Spider *wc_spider; /** private member functions */ void vertexAdd ( const int &v1, const int &val, const int &size, const QString &color, const QString &numColor, const int &numSize, const QString &label, const QString &labelColor, const int &labelSize, const QPointF &p, const QString &shape ); void edgeAdd (const int &v1, const int &v2, const float &weight, const int &type, const QString &label, const QString &color ); /** methods used by graphMatrixDistancesCreate() */ void BFS(const int &s, const bool &computeCentralities=false, const bool &dropIsolates=false); void dijkstra(const int &s, const bool &computeCentralities=false, const bool &inverseWeights=false, const bool &dropIsolates=false); void minmax( float C, Vertex *v, float &max, float &min, int &maxNode, int &minNode ) ; void resolveClasses (float C, H_StrToInt &discreteClasses, int &classes); void resolveClasses ( float C, H_StrToInt &discreteClasses, int &classes, int name ); QList m_relationsList; QList triadTypeFreqs; //stores triad type frequencies QList m_isolatedVerticesList,m_verticesList, m_graphFileFormatExportSupported; QSet m_verticesSet; QList m_selectedEdges; QList m_selectedVertices; QHash influenceRanges, influenceDomains; QHash m_vertexPairsNotConnected; QHash m_vertexPairsUnilaterallyConnected; QMap m_cliques; QList m_clusteringLevel; QMap m_clustersPerSequence; QMap m_clustersByName; QMap m_clusterPairNamesPerSeq; Matrix TM, DM, sumM, invAM, AM, invM; Matrix XM, XSM, XRM, CLQM; stack Stack; /** used in resolveClasses and graphMatrixDistancesCreate() */ H_StrToInt discreteDPs, discreteSDCs, discreteCCs, discreteBCs, discreteSCs; H_StrToInt discreteIRCCs, discreteECs, discreteEccentricities; H_StrToInt discretePCs, discreteICs, discretePRPs, discretePPs, discreteEVCs; int m_precision, m_fieldWidth, m_curRelation, m_fileFormat, m_vertexClicked; ClickedEdge m_clickedEdge; float edgeWeightTemp, edgeReverseWeightTemp; float meanSDC, varianceSDC; float meanSCC, varianceSCC; float meanIRCC, varianceIRCC; float meanSBC, varianceSBC; float meanSSC, varianceSSC; float meanEC, varianceEC; float meanSPC, varianceSPC; float meanIC, varianceIC; float meanEVC, varianceEVC; float meanSDP, varianceSDP; float meanPP, variancePP; float meanPRP, variancePRP; float minEccentricity, maxEccentricity, sumEccentricity; float minSDP, maxSDP, sumDP, sumSDP, groupDP; float minSDC, maxSDC, sumDC, sumSDC, groupDC; float minSCC, maxSCC, nomSCC, denomSCC, sumCC, sumSCC, groupCC, maxIndexCC; float minIRCC, maxIRCC, nomIRCC, denomIRCC, sumIRCC, groupIRCC; float minSBC, maxSBC, nomSBC, denomSBC, sumBC, sumSBC, groupSBC, maxIndexBC; float minSPC, maxSPC, nomSPC, denomSPC, t_sumIC, sumSPC, groupSPC, maxIndexPC; float minSSC, maxSSC, sumSC, sumSSC, groupSC, maxIndexSC; float minEC, maxEC, nomEC, denomEC, sumEC, groupEC, maxIndexEC; float minIC, maxIC, nomIC, denomIC, sumIC, maxIndexIC; float minEVC, maxEVC, nomEVC, denomEVC, sumEVC, sumSEVC, groupEVC; float minPRP, maxPRP, nomPRC, denomPRC, sumPC, t_sumPRP, sumPRP; float minPP, maxPP, nomPP, denomPP, sumPP, groupPP; float minCLC, maxCLC, averageCLC,varianceCLC, d_factor; int maxNodeCLC, minNodeCLC; int classesSDP, maxNodeDP, minNodeDP; int classesSDC, maxNodeSDC, minNodeSDC; int classesSCC, maxNodeSCC, minNodeSCC; int classesIRCC, maxNodeIRCC, minNodeIRCC; int classesSBC, maxNodeSBC, minNodeSBC; int classesSPC, maxNodeSPC, minNodeSPC; int classesSSC, maxNodeSSC, minNodeSSC; int classesEC, maxNodeEC, minNodeEC; int classesEccentricity, maxNodeEccentricity, minNodeEccentricity; int classesIC, maxNodeIC, minNodeIC; int classesPRP, maxNodePRP, minNodePRP; int classesPP, maxNodePP, minNodePP; int classesEVC, maxNodeEVC, minNodeEVC; int sizeOfComponent; /** General & initialisation variables */ int graphModifiedFlag; long int m_totalVertices, m_totalEdges, m_graphDiameter, initVertexSize; int initVertexLabelSize, initVertexNumberSize; int initVertexNumberDistance, initVertexLabelDistance; bool order, initVertexLabelsVisibility,initVertexNumbersVisibility; bool initVertexNumberInside, initEdgeWeightNumbers, initEdgeLabels; float m_graphAverageDistance, m_graphGeodesicsCount; float m_graphDensity; int m_graphConnectedness; int outboundEdgesVert, inboundEdgesVert, reciprocalEdgesVert; int timerId, canvasWidth, canvasHeight; bool calculatedEdges; bool calculatedVertices, calculatedVerticesList, calculatedVerticesSet; bool calculatedAdjacencyMatrix, calculatedDistances, calculatedCentralities; bool calculatedIsolates; bool calculatedEVC; bool calculatedDP, calculatedDC, calculatedPP; bool calculatedIRCC, calculatedIC, calculatedPRP; bool calculatedTriad; bool calculatedGraphSymmetry, calculatedGraphDensity, calculatedGraphWeighted; bool m_undirected, m_symmetric, m_isWeighted; QString VERSION, fileName, m_graphName, initEdgeColor, initVertexColor, initVertexNumberColor, initVertexLabelColor, initVertexShape; QString htmlHead, htmlHeadLight, htmlEnd; QDateTime actualDateTime; }; #endif socnetv-2.2/src/graphicswidget.cpp000755 000765 000024 00000132117 13040734201 020326 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt graphicswidget.cpp description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include "graphicswidget.h" #include #include #include #include #include #include #include "mainwindow.h" #include "node.h" #include "edge.h" #include "nodenumber.h" #include "nodelabel.h" #include "guide.h" #include "edgeweight.h" #include "edgelabel.h" /** Constructor method. Called when a GraphicsWidget object is created in MW */ GraphicsWidget::GraphicsWidget( QGraphicsScene *sc, MainWindow* par) : QGraphicsView ( sc,par) { setScene(sc); secondDoubleClick=false; transformationActive = false; m_nodeLabel=""; m_zoomIndex=250; m_currentScaleFactor = 1; m_currentRotationAngle = 0; markedNodeExist=false; //used in findNode() clickedEdgeExists = false; //used in selecting and edge edgesHash.reserve(1000); nodeHash.reserve(1000); connect ( sc , &QGraphicsScene::selectionChanged, this, &GraphicsWidget::getSelectedItems); } /** http://thesmithfam.org/blog/2007/02/03/qt-improving-qgraphicsview-performance/#comment-7215 */ void GraphicsWidget::paintEvent ( QPaintEvent * event ){ // qDebug()<<"GraphicsWidget::paintEvent "; // QPaintEvent *newEvent=new QPaintEvent(event->region().boundingRect()); // QGraphicsView::paintEvent(newEvent); // delete newEvent; QGraphicsView::paintEvent(event); } /** * @brief GraphicsWidget::clear * Clears the scene and all hashes, lists, var etc */ void GraphicsWidget::clear() { nodeHash.clear(); edgesHash.clear(); m_selectedNodes.clear(); m_selectedEdges.clear(); scene()->clear(); m_curRelation=0; markedNodeExist=false; clickedEdgeExists = false; firstNode=0; secondDoubleClick=false; qDebug() << "GW::clear() - finished clearing hashes"; } /** * @brief GraphicsWidget::relationSet * Called from Graph::signalRelationChangedToGW(int) signal * @param relation */ void GraphicsWidget::relationSet(int relation) { qDebug() << "GraphicsWidget::relationSet() to " << relation; m_curRelation = relation; } /** * @brief GraphicsWidget::drawNode * Adds a new node onto the scene * Called from Graph::vertexCreate method primarily when we load files * It is also called in the end when the user presses "Add Node" button or * the user double clicks (mouseDoubleClickEvent() calls Graph::vertexCreate) * @param num * @param nodeSize * @param nodeColor * @param numberColor * @param numberSize * @param nodeLabel * @param labelColor * @param labelSize * @param p * @param nodeShape * @param showLabels * @param numberInsideNode * @param showNumbers */ void GraphicsWidget::drawNode( const int &num, const int &nodeSize, const QString &nodeShape, const QString &nodeColor, const bool &showNumbers,const bool &numberInsideNode, const QString &numberColor, const int &numberSize, const int &numberDistance, const bool &showLabels, const QString &nodeLabel, const QString &labelColor, const int &labelSize, const int &labelDistance, const QPointF &p ) { qDebug()<< "GW: drawNode(): drawing new node " << num << " at: " << p.x() << ", "<< p.y() ; //Draw node Node *jim= new Node ( this, num, nodeSize, nodeColor, nodeShape, showNumbers, numberInsideNode, numberColor, numberSize, numberDistance, showLabels, nodeLabel, labelColor, labelSize, labelDistance, p ); //add new node to a container to ease finding, edge creation etc nodeHash.insert(num, jim); } /** * @brief GraphicsWidget::drawEdge * Draws an edge from source to target Node. * Used when we do not have references to nodes but only nodeNumbers: a) when we load a network file b) when the user clicks on the AddLink button on the MW. * @param source * @param target * @param weight * @param reciprocal * @param drawArrows * @param color * @param bezier */ void GraphicsWidget::drawEdge(const int &source, const int &target, const float &weight, const QString &label, const QString &color, const int &type, const bool &drawArrows, const bool &bezier, const bool &weightNumbers){ edgeName = QString::number(m_curRelation) + QString(":") + QString::number(source) + QString(">")+ QString::number(target); qDebug()<<"GW::drawEdge() - "<< edgeName << " weight "<")+ QString::number(source); qDebug("GW::drawEdge() - making existing edge between %i and %i reciprocal. Name: "+edgeName.toUtf8(), source, target ); edgesHash.value(edgeName)->setDirectedWithOpposite(); } // qDebug()<< "Scene items now: "<< scene()->items().size() << " - GW items now: "<< items().size(); } /** Called when the user middle-clicks on two nodes consecutively. . It saves the source & target nodes that were clicked On the second middle-click, it emits the userMiddleClicked() signal to MW, which will notify activeGraph, which in turn will signal back to drawEdge()... */ void GraphicsWidget::startEdge(Node *node){ if (secondDoubleClick){ qDebug()<< "GW::startEdge() - this is the second double click. " "Emitting userMiddleClicked() to create edge"; secondNode=node; emit userMiddleClicked(firstNode->nodeNumber(), secondNode->nodeNumber() ); //( (MainWindow*)parent() )->setCursor(Qt::ArrowCursor); emit setCursor(Qt::ArrowCursor); secondDoubleClick=false; } else{ qDebug()<<"GW::startEdge() - this is the first double click."; firstNode=node; secondDoubleClick=true; //( (MainWindow*)parent() )->setCursor( Qt::PointingHandCursor); emit setCursor( Qt::PointingHandCursor ); } } /** This is called from each node when the user clicks on it. It emits the userClickedNode signal to MW which is used to - display node info on the status bar - notify context menus for the clicked node. */ void GraphicsWidget::nodeClicked(Node *node){ qDebug () << "GW::nodeClicked() - Emitting userClickedNode()"; if (clickedEdgeExists) edgeClicked(0); emit userClickedNode(node->nodeNumber()); } /** This is called from each edge when the user clicks on it. It emits the userClickedEdge signal to Graph which is used to - display edge info on the status bar - notify context menus for the clicked edge. Also, it makes source and target nodes to stand out of other nodes. */ void GraphicsWidget::edgeClicked(Edge *edge){ //qDebug() <<"GW::edgeClicked()"; if (clickedEdgeExists) { //unselect them, restore their color markedEdgeSource->setSelected(false); markedEdgeTarget->setSelected(false); //restore their size markedEdgeSource->setSize(markedEdgeSourceOrigSize); markedEdgeTarget->setSize(markedEdgeTargetOrigSize); clickedEdgeExists=false; return; } markedEdgeSource=edge->sourceNode(); markedEdgeTarget=edge->targetNode(); clickedEdgeExists=true; // select nodes to change their color markedEdgeSource->setSelected(true); markedEdgeTarget->setSelected(true); // save their original size markedEdgeSourceOrigSize=markedEdgeSource->size(); markedEdgeTargetOrigSize=markedEdgeTarget->size(); //now, make them larger markedEdgeSource->setSize(2*markedEdgeSourceOrigSize-1); markedEdgeTarget->setSize(2*markedEdgeTargetOrigSize-1); emit userClickedEdge(edge->sourceNode()->nodeNumber(), edge->targetNode()->nodeNumber() ); } /** * @brief GraphicsWidget::moveNode * Called from activeGraph to update node coordinates on the canvas * @param num * @param x * @param y */ void GraphicsWidget::moveNode(const int &num, const qreal &x, const qreal &y){ qDebug() << " GW: moveNode() " << num << ": " << x << y; nodeHash.value(num)->setPos(x,y); // qDebug() << "GW: moveNode() node reports x, y as " // << nodeHash.value(number)->x() << nodeHash.value(number)->x(); } /** * @brief GraphicsWidget::eraseNode * Called from Graph signal eraseNode(int) * @param number */ void GraphicsWidget::eraseNode(const long int &number){ qDebug() << "GW::eraseNode() - node " << number << " scene items: " << scene()->items().size() << " view items: " << items().size() << " nodeHash items: "<< nodeHash.count(); if ( nodeHash.contains(number) ) { qDebug() << "GW::eraseNode() - found number " << number<< " Deleting :)" ; delete nodeHash.value(number); } qDebug() << "GW::eraseNode() - node erased! " << " scene items now: " << scene()->items().size() << " view items: " << items().size() << " nodeHash items: "<< nodeHash.count() << " edgesHash items: "<< edgesHash.count() ; } /** * @brief GraphicsWidget::eraseEdge * Called from MW/Graph when erasing edges using vertex numbers * Also called when transforming directed edges to undirected. * @param sourceNode * @param targetNode */ void GraphicsWidget::eraseEdge(const long int &source, const long int &target){ qDebug() << "GW::eraseEdge(): " << source << " -> " << target << " scene items: " << scene()->items().size() << " view items: " << items().size() << " edgesHash.count: " << edgesHash.count(); edgeName = QString::number(m_curRelation) + QString(":") + QString::number( source ) + QString(">") + QString::number( target ); if ( edgesHash.contains(edgeName) ) { delete edgesHash.value(edgeName); } qDebug() << "GW::eraseEdge() - deleted " << " scene items: " << scene()->items().size() << " view items: " << items().size() << " edgesHash.count: " << edgesHash.count(); } /** * @brief GraphicsWidget::removeItem * @param node * Called from Node::~Node() to remove itself from nodeHash, scene and * be deleted */ void GraphicsWidget::removeItem( Node *node){ long int i=node->nodeNumber(); qDebug() << "GW::removeItem(node) - number: " << i; if (firstNode == node) { qDebug() << "GW::removeItem(node) - number: " << i << "previously set as source node for a new edge. Unsetting."; secondDoubleClick = false; //( (MainWindow*)parent() )->setCursor(Qt::ArrowCursor); emit setCursor(Qt::ArrowCursor); } nodeHash.remove(i); scene()->removeItem(node); node->deleteLater (); qDebug() << "GW::removeItem(node) - node erased! " << " scene items now: " << scene()->items().size() << " view items: " << items().size(); } /** * @brief GraphicsWidget::removeItem * @param edge * Called from Edge::~Edge() to remove itself from edgesHash and scene and * be deleted */ void GraphicsWidget::removeItem( Edge * edge){ qDebug() << "GW::removeItem(edge)" ; edgeName = QString::number(m_curRelation) + QString(":") + QString::number( edge->sourceNodeNumber() ) + QString(">") + QString::number( edge->targetNodeNumber() ); edgesHash.remove(edgeName); scene()->removeItem(edge); edge->deleteLater(); qDebug() << "GW::removeItem(edge) - edge erased! " << " scene items now: " << scene()->items().size() << " view items: " << items().size(); } /** * @brief GraphicsWidget::removeItem * @param edgeWeight */ void GraphicsWidget::removeItem( EdgeWeight *edgeWeight){ qDebug() << "GW::removeItem(edgeWeight) - of edge" << "GW items now: " << items().size(); scene()->removeItem(edgeWeight); edgeWeight->deleteLater(); qDebug() << "GW::removeItem(edgeWeight) - edgeWeight erased! " << " scene items now: " << scene()->items().size() << " view items: " << items().size(); } /** * @brief GraphicsWidget::removeItem * @param edgeLabel */ void GraphicsWidget::removeItem( EdgeLabel *edgeLabel){ qDebug() << "GW::removeItem(edgeLabel) - of edge" << "GW items now: " << items().size(); scene()->removeItem(edgeLabel); edgeLabel->deleteLater(); qDebug() << "GW::removeItem(edgeLabel) - edgeLabel erased! " << " scene items now: " << scene()->items().size() << " view items: " << items().size(); } /** * @brief GraphicsWidget::removeItem * @param nodeLabel */ void GraphicsWidget::removeItem( NodeLabel *nodeLabel){ qDebug() << "GW::removeItem(label) - of node " << nodeLabel->node()->nodeNumber() << "GW items now: " << items().size(); scene()->removeItem(nodeLabel); nodeLabel->deleteLater(); qDebug() << "GW::removeItem(label) - label erased! " << " scene items now: " << scene()->items().size() << " view items: " << items().size(); } /** * @brief GraphicsWidget::removeItem * @param nodeNumber */ void GraphicsWidget::removeItem( NodeNumber *nodeNumber){ qDebug() << "GW::removeItem(number) - of node " << nodeNumber->node()->nodeNumber() << "GW items now: " << items().size(); scene()->removeItem(nodeNumber); nodeNumber->deleteLater(); qDebug() << "GW::removeItem(number) - number erased! " << " scene items now: " << scene()->items().size() << " view items: " << items().size(); } /** * @brief GraphicsWidget::setNodeColor * Sets the color of an node. * Called from MW when the user changes the color of a node (right-clicking). * @param nodeNumber * @param color * @return */ bool GraphicsWidget::setNodeColor(const long int &nodeNumber, const QString &color){ qDebug() << "GW::setNodeColor() : " << color; nodeHash.value(nodeNumber) -> setColor(color); return true; } /** Sets the shape of an node. Called from MW when the user changes the shape of a node */ bool GraphicsWidget::setNodeShape(const long &nodeNumber, const QString &shape){ qDebug() << "GW::setNodeShape() : " << shape; nodeHash.value(nodeNumber) -> setShape(shape); return true; } void GraphicsWidget::setNodeNumberVisibility(const bool &toggle){ qDebug()<< "GW::setNodeNumberVisibility()" << toggle; foreach ( Node *m_node, nodeHash) { m_node->setNumberVisibility(toggle); } } void GraphicsWidget::setNodeLabelsVisibility (const bool &toggle){ qDebug()<< "GW::setNodeLabelsVisibility()" << toggle; foreach ( Node *m_node, nodeHash) { m_node->setLabelVisibility(toggle); } } /** * @brief GraphicsWidget::setNodeLabel * Sets the label of an node. Called from MW. * @param nodeNumber * @param label * @return */ bool GraphicsWidget::setNodeLabel(long int nodeNumber, QString label){ qDebug() << "GW::setNodeLabel() : " << label; nodeHash.value(nodeNumber) -> setLabelText (label); return true; } /** * @brief GraphicsWidget::setNumbersInsideNodes * Toggles node numbers displayed inside or out of nodes * Called from MW * @param numIn */ void GraphicsWidget::setNumbersInsideNodes(bool numIn){ qDebug()<< "GW::setNumbersInsideNodes" << numIn; foreach ( Node *m_node, nodeHash) { m_node->setNumberInside(numIn); } } /** * @brief GraphicsWidget::setEdgeLabel * Sets the label of an edge. * Called from MW when the user changes the label of an edge (right-clicking). * @param source * @param target * @param label */ void GraphicsWidget::setEdgeLabel(const long int &source, const long int &target, const QString &label){ QString edgeName = QString::number(m_curRelation) + QString(":") + QString::number( source ) + QString(">")+ QString::number( target ); qDebug()<<"GW::setEdgeLabel() -" << edgeName << " new label " << label; if ( edgesHash.contains (edgeName) ) { edgesHash.value(edgeName) -> setLabel(label); } } /** * @brief GraphicsWidget::setEdgeColor * Sets the color of an edge. * Called from MW when the user changes the color of an edge (right-clicking). * Also called from Graph when all edge colors need to be changed. * @param source * @param target * @param color */ void GraphicsWidget::setEdgeColor(const long int &source, const long int &target, const QString &color){ QString edgeName = QString::number(m_curRelation) + QString(":") + QString::number( source ) + QString(">")+ QString::number( target ); qDebug()<<"GW::setEdgeColor() -" << edgeName << " new color " << color; if ( edgesHash.contains (edgeName) ) { edgesHash.value(edgeName) -> setColor(color); } } /** * @brief GraphicsWidget::setEdgeUndirected * Makes an edge undirected * @param source * @param target * @param weight * @return */ bool GraphicsWidget::setEdgeUndirected(const long int &source, const long int &target, const float &weight){ qDebug() << "GW::setEdgeUndirected() : " << source << "->" << target << " = " << weight; QString edgeName = QString::number(m_curRelation) + QString(":") + QString::number( source ) + QString(">")+ QString::number( target ); qDebug()<<"GW::setEdgeUndirected() - checking edgesHash for:" << edgeName ; if ( edgesHash.contains (edgeName) ) { qDebug()<<"GW::setEdgeUndirected() - edge exists in edgesHash. " << " Transforming it to undirected"; edgesHash.value(edgeName) -> setUndirected(); qDebug()<<"GW::setEdgeUndirected() - removing opposite edge " << target << " -> " << source ; eraseEdge(target, source); return true; } return false; } /** Changes/Sets the weight of an edge. Called from MW when the user changes the weight of an edge (right-clicking). */ bool GraphicsWidget::setEdgeWeight(const long int &source, const long int &target, const float &weight){ qDebug() << "GW::setEdgeWeight() : " << source << "->" << target << " = " << weight; QString edgeName = QString::number(m_curRelation) + QString(":") + QString::number( source ) + QString(">")+ QString::number( target ); qDebug()<<"GW::setEdgeWeight() -" << edgeName << " new weight " << weight; if ( edgesHash.contains (edgeName) ) { edgesHash.value(edgeName) -> setWeight(weight); return true; } return false; } void GraphicsWidget::setEdgeWeightNumbersVisibility (const bool &toggle){ qDebug()<< "GW::setEdgeWeightNumbersVisibility()" << toggle; foreach ( Edge *m_edge, edgesHash) { m_edge->setWeightNumberVisibility(toggle); } } void GraphicsWidget::setEdgeLabelsVisibility (const bool &toggle){ qDebug()<< "GW::setEdgeLabelsVisibility()" << toggle << "edgesHash.count: " << edgesHash.count(); foreach ( Edge *m_edge, edgesHash) { m_edge->setLabelVisibility(toggle); } } /** Sets initial node size from MW. It is Called from MW on startup and when user changes it. */ void GraphicsWidget::setInitNodeSize(int size){ qDebug()<< "GW::setInitNodeSize() " << size; m_nodeSize=size; } /** * @brief GraphicsWidget::setInitZoomIndex * Passes initial zoom setting * Called from MW on startup */ void GraphicsWidget::setInitZoomIndex(int zoomIndex) { m_zoomIndex = zoomIndex; } /** * Changes the visibility of an GraphicsView edge (number, label, edge, etc) */ void GraphicsWidget::setEdgeVisibility(int relation, int source, int target, bool toggle){ QString edgeName = QString::number(relation) + QString(":") + QString::number( source ) + QString(">")+ QString::number( target ); if ( edgesHash.contains (edgeName) ) { qDebug()<<"GW: setEdgeVisibility(). relation " << relation << " : " << source << " -> "<< target << " to " << toggle; edgesHash.value(edgeName) -> setVisible(toggle); edgesHash.value(edgeName) -> setEnabled(toggle); return; } qDebug()<<"GW: setEdgeVisibility(). Cannot find edge " << relation << " : " << source << " -> "<< target ; } /** * Changes the visibility of a Node */ void GraphicsWidget::setNodeVisibility(long int number, bool toggle){ if ( nodeHash.contains (number) ) { qDebug() << "GW: setNodeVisibility(): for " << number << " to " << toggle; nodeHash.value(number) -> setVisible(toggle); nodeHash.value(number) -> setEnabled(toggle); return; } qDebug() << "GW: setNodeVisibility(): cannot find node " << number; } /** * @brief GraphicsWidget::setNodeSize * @param number * @param size * @return */ bool GraphicsWidget::setNodeSize(const long int &number, const int &size ){ qDebug () << " GraphicsWidget::setNodeSize() node: "<< number << " new size "<< size; if ( nodeHash.contains (number) ) { if (size>0){ qDebug() << "GW: setNodeSize(): for "<< number << " to " << size ; nodeHash.value(number) -> setSize(size); return true; } else { qDebug() << "GW: setNodeSize(): for "<< number << " to initial size" << m_nodeSize; nodeHash.value(number) -> setSize(m_nodeSize); return true; } } qDebug() << "GW: setNodeSize(): cannot find node " << number; return false; } /** * @brief GraphicsWidget::setAllNodeSize * @param size * @return */ void GraphicsWidget::setAllNodeSize(const int &size ){ qDebug() << "GW: setAllNodeSize() "; foreach ( Node *m_node, nodeHash ) { qDebug() << "GW: setAllNodeSize(): "<< m_node->nodeNumber() << " to new size " << size ; m_node -> setSize(size); } } /** * @brief GraphicsWidget::setNodeNumberSize * @param number * @param size */ bool GraphicsWidget::setNodeNumberSize(const long int &number, const int &size){ qDebug () << " GraphicsWidget::setNodeNumberSize() node number: "<< number << " new number size "<< size; if ( nodeHash.contains (number) ) { if (size>0){ qDebug() << "GW: setNodeNumberSize(): for "<< number << " to " << size ; nodeHash.value(number) ->setNumberSize(size) ; return true; } } qDebug() << "GW: setNodeSize(): cannot find node " << number; return false; } /** * @brief GraphicsWidget::setNodeNumberDistance * @param number * @param distance */ bool GraphicsWidget::setNodeNumberDistance( const long int &number, const int &distance ){ qDebug () << "GW::setNodeNumberDistance() node number: "<< number << " new number distance "<< distance; if ( nodeHash.contains (number) ) { if (distance>=0){ qDebug() << "GW::setNodeNumberDistance(): for "<< number << " to " << distance ; nodeHash.value(number) ->setNumberDistance(distance) ; return true; } } qDebug() << "GW::setNodeNumberSize(): cannot find node " << number; return false; } /** * @brief GraphicsWidget::setNodeLabelColor * @param number * @param color */ bool GraphicsWidget::setNodeLabelColor(const long int &number, const QString &color){ qDebug () << "GW::setNodeLabelColor() - node number: "<< number << " new Label color"<< color; if ( nodeHash.contains (number) ) { nodeHash.value(number) ->setLabelColor(color); return true; } qDebug() << "GW:setNodeLabelColor() - cannot find node " << number; return false; } /** * @brief GraphicsWidget::setNodeLabelSize * @param number * @param size */ bool GraphicsWidget::setNodeLabelSize(const long int &number, const int &size){ qDebug () << "GW::setNodeLabelSize() - node number: "<< number << " new Label size "<< size; if ( nodeHash.contains (number) ) { if (size>0){ qDebug() << "GW::setNodeLabelSize(): for "<< number << " to " << size ; nodeHash.value(number) ->setLabelSize(size); return true; } } qDebug() << "GW:setNodeLabelSize() - cannot find node " << number; return false; } /** * @brief GraphicsWidget::setNodeLabelDistance * @param number * @param distance */ bool GraphicsWidget::setNodeLabelDistance( const long int &number, const int &distance ){ qDebug () << "GW::setNodeLabelDistance() - node number: "<< number << " new label distance "<< distance; if ( nodeHash.contains (number) ) { if (distance>=0){ qDebug() << "GW::setNodeLabelDistance(): for "<< number << " to " << distance ; nodeHash.value(number) ->setLabelDistance(distance) ; return true; } } qDebug() << "GW::setNodeLabelDistance() - cannot find node " << number; return false; } /* * Used by findNode. * Returns, if found, the node with label or number 'text' */ Node* GraphicsWidget::hasNode( QString text ){ bool ok = false; foreach ( Node *candidate, nodeHash) { if ( candidate->nodeNumber()==text.toInt(&ok, 10) || ( candidate->labelText() == text) ) { qDebug() << "GW: hasNode(): Node " << text << " found!"; markedNodeExist=true; return candidate; break; } } return markedNode1; //dummy return. We check markedNodeExist flag first... } /** Marks (by double-sizing and highlighting) or unmarks a node, given its number or label. Called by MW:slotEditNodeFind() */ bool GraphicsWidget::setMarkedNode(QString nodeText){ qDebug ("GW: setMarkedNode()"); if (markedNodeExist) { markedNode1->setSelected(false); //unselect it, so that it restores its color markedNode1->setSize(markedNodeOrigSize); //restore its size markedNodeExist=false; return true; } markedNode1 = hasNode (nodeText); if (!markedNodeExist) return false; markedNode1->setSelected(true); //select it, so that it changes color markedNodeOrigSize=markedNode1->size(); // save its original size markedNode1->setSize(2*markedNodeOrigSize-1); //now, make it larger return true; } /** * Changes the visibility of all items of certain type (i.e. number, label, edge, etc) */ void GraphicsWidget::setAllItemsVisibility(int type, bool visible){ QList list = scene()->items(); for (QList::iterator item=list.begin();item!=list.end(); item++) { qDebug()<< "GW::setAllItemsVisibility. item type is " << (*item)->type(); if ( (*item)->type() == type){ if (visible) (*item)->show(); else (*item)->hide(); } } } void GraphicsWidget::addGuideCircle( const double&x0, const double&y0, const double&radius){ Guide *circ=new Guide (this, x0, y0, radius); circ->show(); } void GraphicsWidget::addGuideHLine(const double &y0){ Guide *line=new Guide (this, y0, this->width()); line->show(); } /** * Removes all items of certain type (i.e. number, label, edge, etc) */ void GraphicsWidget::removeAllItems(int type){ qDebug()<< "GW: removeAllItems"; QList list = scene()->items(); for (QList::iterator item=list.begin();item!=list.end(); item++) { if ( (*item)->type() == type){ Guide *guide = qgraphicsitem_cast (*item); qDebug()<< "GW: removeAllItems - located element"; guide->die(); guide->deleteLater (); delete *item; } } } void GraphicsWidget::clearGuides(){ qDebug()<< "GW: clearGuides"; this->removeAllItems(TypeGuide); } /** * @brief GraphicsWidget::selectAll * Called from MW. Clears any clickedNode info and sets a selection rect * in the scene, which signals QGraphicsScene::selectionChanged signal to update * selectedNodes and selectedEdges. */ void GraphicsWidget::selectAll(){ QPainterPath path; path.addRect(0,0, this->scene()->width() , this->scene()->height()); this->scene()->setSelectionArea(path); emit userClickedNode(0); qDebug() << "GraphicsWidget::selectAll() - selected items now: " << selectedItems().count(); } /** * @brief GraphicsWidget::selectNone * Called from MW. Clears any clickedNode info and any previous selection rect * in the scene, which again signals selectionChanged() to update selectedNodes * and selectedEdges to zero. */ void GraphicsWidget::selectNone(){ qDebug() << "GraphicsWidget::selectNone()"; emit userClickedNode(0); this->scene()->clearSelection(); } /** * @brief Emits selected nodes and edges to Graph and MW * Called by QGraphicsScene::selectionChanged signal whenever the user * makes a selection on the canvas. * Emits selectedNodes and selectedEdges lists to * Graph::graphSelectionChanged() which then signals to * MW::slotEditSelectionChanged to display counts on app window. */ void GraphicsWidget::getSelectedItems() { qDebug() <<"GW::getSelectedItems()"; if (!clickedEdgeExists) // emit userSelectedItems(nodes, edges); emit userSelectedItems(selectedNodes(), selectedEdges()); } /** * @brief Returns a QList of all selected QGraphicsItem(s) * @return a QList of all selected QGraphicsItem(s) */ QList GraphicsWidget::selectedItems(){ qDebug() <<"GW::selectedItems()"; return this->scene()->selectedItems(); } /** * @brief Returns a QList of selected node numbers * Called by GW::getSelectedItems and MW::selectedNodes * @return a QList of integers: the selected node numbers */ QList GraphicsWidget::selectedNodes() { m_selectedNodes.clear(); foreach (QGraphicsItem *item, scene()->selectedItems()) { if (Node *node = qgraphicsitem_cast(item) ) { m_selectedNodes.append(node->nodeNumber()); } } return m_selectedNodes; } /** * @brief Returns a QList of selected directed edges structs in the form of v1,v2 * * @return a QList of selected directed edges structs */ QList GraphicsWidget::selectedEdges() { m_selectedEdges.clear(); foreach (QGraphicsItem *item, scene()->selectedItems()) { if (Edge *edge= qgraphicsitem_cast(item) ) { SelectedEdge selEdge = make_pair( edge->sourceNodeNumber(), edge->targetNodeNumber()); m_selectedEdges << selEdge; } } return m_selectedEdges; } /** Starts a new node when the user double-clicks somewhere Emits userDoubleClicked to MW::slotEditNodeAddWithMouse() which - displays node info on MW status bar and - calls Graph::vertexCreate(), which in turn calls this->drawNode()... Yes, we make a full circle! :) */ void GraphicsWidget::mouseDoubleClickEvent ( QMouseEvent * e ) { if ( QGraphicsItem *item= itemAt(e->pos() ) ) { if (Node *node = qgraphicsitem_cast(item)) { qDebug() << "GW: mouseDoubleClickEvent() - on a node!" << "Scene items: "<< scene()->items().size() << "GW items: " << items().size() << "Starting new edge!"; node->setSelected(true); nodeClicked(node); startEdge(node); QGraphicsView::mouseDoubleClickEvent(e); return; } else if ( (*item).type() == TypeLabel){ QGraphicsView::mouseDoubleClickEvent(e); return; } qDebug() << "GW: mouseDoubleClickEvent() - on something, not a node!" << "Scene items: "<< scene()->items().size() << "GW items: " << items().size(); } QPointF p = mapToScene(e->pos()); qDebug()<< "GW::mouseDoubleClickEvent() - on empty space. " << "Scene items: "<< scene()->items().size() << " GW items: " << items().size() << " Signaling MW to create a new vertex in graph. e->pos() " << e->pos().x() << ","<< e->pos().y() << ", "<< p.x() << "," <pos()); // bool ctrlKey = (e->modifiers() == Qt::ControlModifier); // emit selectedItems(m_selectedItems); if ( QGraphicsItem *item= itemAt(e->pos() ) ) { if (Node *node = qgraphicsitem_cast(item)) { qDebug() << "GW::mousePressEvent() - click at " << e->pos().x() << ","<< e->pos().y() << " or "<< p.x() << ","<< p.y() << " selectedItems " << selectedItems().count() << "Single click on a node. " << "Setting selected and emitting nodeClicked"; node->setSelected(true); nodeClicked(node); if ( e->button()==Qt::RightButton ) { qDebug() << "GW::mousePressEvent() - Right-click on node. " << "emitting openNodeMenu() "; emit openNodeMenu(); } if ( e->button()==Qt::MidButton) { qDebug() << "GW::mousePressEvent() - Middle-click on node. " << "Calling startEdge() "; startEdge(node); } QGraphicsView::mousePressEvent(e); return; } if (Edge *edge= qgraphicsitem_cast(item)) { qDebug() << "GW::mousePressEvent() - click at " << e->pos().x() << ","<< e->pos().y() << " or "<< p.x() << ","<< p.y() << " selectedItems " << selectedItems().count() << "Single click on an edge. " << "Emitting edgeClicked"; edgeClicked(edge); if ( e->button()==Qt::LeftButton ) { qDebug() << "GW::mousePressEvent() - left click on an edge "; // graphicsWidget->startNodeMovement(0); } else if ( e->button()==Qt::RightButton ) { qDebug() << "GW::mousePressEvent() - right click on an edge." << "Emitting openEdgeContextMenu()"; emit openEdgeMenu(); } QGraphicsView::mousePressEvent(e); return; } } // if ( selectedItems().count() > 0 && ctrlKey ) { // qDebug() << "GW::mousePressEvent() - opening selection context menu "; // emit openContextMenu(p); // } else if ( e->button()==Qt::RightButton ) { qDebug() << "GW::mousePressEvent() - click at " << e->pos().x() << ","<< e->pos().y() << " or "<< p.x() << ","<< p.y() << " selectedItems " << selectedItems().count() << "Right click on empty space. Emitting openContextMenu()"; emit openContextMenu(p); } else { qDebug() << "GW::mousePressEvent() - click at " << e->pos().x() << ","<< e->pos().y() << " or "<< p.x() << ","<< p.y() << " selectedItems " << selectedItems().count() << "Right click on empty space. Emitting userClickOnEmptySpace()"; emit userClickOnEmptySpace(p); } QGraphicsView::mousePressEvent(e); } /** * @brief GraphicsWidget::mouseReleaseEvent * @param e * Called when user releases a mouse button, after a click. * First sees what was in the canvas position where the user clicked * If a node was underneath, it calls userNodeMoved() signal for every node * in scene selectedItems */ void GraphicsWidget::mouseReleaseEvent( QMouseEvent * e ) { QPointF p = mapToScene(e->pos()); if ( QGraphicsItem *item= itemAt(e->pos() ) ) { if (Node *node = qgraphicsitem_cast(item)) { qDebug() << "GW::mouseReleaseEvent() at " << e->pos().x() << ","<< e->pos().y() << " or "<< p.x() << ","<selectedItems()) { if (Node *nodeSelected = qgraphicsitem_cast(item) ) { emit userNodeMoved(nodeSelected->nodeNumber(), nodeSelected->x(), nodeSelected->y()); } } QGraphicsView::mouseReleaseEvent(e); } if (Edge *edge= qgraphicsitem_cast(item)) { Q_UNUSED(edge); qDebug() << "GW::mouseReleaseEvent() at " << e->pos().x() << ","<< e->pos().y() << " or "<< p.x() << ","<pos().x() << ","<< e->pos().y() << " or "<< p.x() << ","<modifiers() == Qt::ControlModifier); qDebug("GW: Mouse wheel event"); qDebug() << "GW: delta = " << e->delta(); if (ctrlKey) { float m_scale = e->delta() / qreal(600); qDebug("GW: m_scale = %f", m_scale); if ( m_scale > 0) zoomIn(1); else if ( m_scale < 0) zoomOut(1); else m_scale=1; } } /** * @brief GraphicsWidget::zoomOut * @param level * Called from MW magnifier button */ void GraphicsWidget::zoomOut (int level){ qDebug() << "GW: ZoomOut(): zoom index "<< m_zoomIndex << " - level " << level; m_zoomIndex-=level; if (m_zoomIndex <= 0) { m_zoomIndex = 0; } emit zoomChanged(m_zoomIndex); } /** * @brief GraphicsWidget::zoomIn * @param level * Called from MW magnifier button */ void GraphicsWidget::zoomIn(int level){ qDebug() << "GW: ZoomIn(): index "<< m_zoomIndex << " + level " << level; m_zoomIndex+=level; if (m_zoomIndex > 500) { m_zoomIndex=500; } if (m_zoomIndex < 0) { m_zoomIndex = 0; } emit zoomChanged(m_zoomIndex); } /** * @brief GraphicsWidget::changeMatrixScale * @param value * Initiated from MW zoomSlider and rotateSlider widgets */ void GraphicsWidget::changeMatrixScale(int value) { transformationActive = true; qreal scaleFactor = pow(qreal(2), ( value - 250) / qreal(50) ); m_currentScaleFactor = scaleFactor ; qDebug() << "GW: changeMatrixScale(): value " << value << " m_currentScaleFactor " << m_currentScaleFactor << " m_currentRotationAngle " << m_currentRotationAngle; resetMatrix(); scale(m_currentScaleFactor, m_currentScaleFactor); rotate(m_currentRotationAngle); } /** * @brief GraphicsWidget::rotateLeft */ void GraphicsWidget::rotateLeft(){ m_currentRotationAngle-=5; emit rotationChanged(m_currentRotationAngle); } /** * @brief GraphicsWidget::rotateRight */ void GraphicsWidget::rotateRight() { m_currentRotationAngle+=5; emit rotationChanged(m_currentRotationAngle); } /** * @brief GraphicsWidget::changeMatrixRotation * @param angle */ void GraphicsWidget::changeMatrixRotation(int angle){ transformationActive = true; m_currentRotationAngle = angle; qDebug() << "GW: changeMatrixRotation(): angle " << angle << " m_currentRotationAngle " << m_currentRotationAngle << " m_currentScaleFactor " << m_currentScaleFactor; resetMatrix(); scale(m_currentScaleFactor, m_currentScaleFactor); rotate(angle); } /** * @brief GraphicsWidget::reset * Resets the transformation matrix to the identity matrix ( default zoom and scale ) */ void GraphicsWidget::reset() { m_currentRotationAngle=0; m_currentScaleFactor = 1; m_zoomIndex=250; emit zoomChanged(m_zoomIndex); emit rotationChanged(m_currentRotationAngle); } /** * @brief GraphicsWidget::resizeEvent * @param e * Repositions guides then emits resized() signal to MW and eventually Graph * which does the node repositioning maintaining proportions */ void GraphicsWidget::resizeEvent( QResizeEvent *e ) { if (transformationActive) { transformationActive = false; return; } int w=e->size().width(); int h=e->size().height(); int w0=e->oldSize().width(); int h0=e->oldSize().height(); fX= (double)(w)/(double)(w0); fY= (double)(h)/(double)(h0); foreach (QGraphicsItem *item, scene()->items()) { if ( (item)->type() == TypeGuide ){ if (Guide *guide = qgraphicsitem_cast (item) ) { if (guide->isCircle()) { guide->die(); guide->deleteLater (); delete item; } else { qDebug()<< "GW::resizeEvent() - Horizontal Guide " << " original position (" << guide->x() << "," << guide->y() << ") - width " << guide->width() << ") - will move to (" << guide->x()*fX << ", " << guide->y()*fY << ")" << " new width " << (int) ceil( (guide->width() *fX )); guide->setHorizontalLine( mapToScene(guide->pos().x()*fX, guide->pos().y()*fY), (int) ceil( (guide->width() *fX ))); } } } } //update the scene width and height with that of the graphicsWidget scene()->setSceneRect(0, 0, (qreal) ( w ), (qreal) ( h ) ); qDebug () << "GW::resizeEvent() - old size: (" << w0 << "," << h0 << ") - new size: (" << w << "," << h << ")" << " fX,fY: (" << fX << ","<< fY << ") scene size: (" << scene()->width() << "," << scene()->height() << ")"; emit resized( w , h ); } /** Destructor. */ GraphicsWidget::~GraphicsWidget(){ clear(); } socnetv-2.2/src/graphicswidget.h000755 000765 000024 00000016510 13040734202 017772 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt graphicswidget.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef GRAPHICSWIDGET_H #define GRAPHICSWIDGET_H #include class MainWindow; class Node; class Edge; class NodeNumber; class NodeLabel; class Guide; class EdgeWeight; class EdgeLabel; typedef QHash H_StrToEdge; typedef QHash H_NumToNode; using namespace std; typedef pair SelectedEdge; class GraphicsWidget : public QGraphicsView { Q_OBJECT public: GraphicsWidget(QGraphicsScene*, MainWindow* parent); ~GraphicsWidget(); void clear(); void setInitNodeSize(int); void setInitZoomIndex (int); Node* hasNode(QString text); bool setMarkedNode(QString text); QList selectedItems(); QList selectedNodes(); QList selectedEdges(); void selectAll(); void selectNone(); void removeItem(Edge*); void removeItem(EdgeWeight *edgeWeight); void removeItem(EdgeLabel *edgeLabel); void removeItem(Node*); void removeItem(NodeNumber*); void removeItem(NodeLabel*); void setNumbersInsideNodes(bool); void setAllItemsVisibility(int, bool); void removeAllItems(int); protected: void wheelEvent(QWheelEvent *event); void mouseDoubleClickEvent ( QMouseEvent * e ); void mousePressEvent ( QMouseEvent * e ); void mouseReleaseEvent(QMouseEvent * e ); void resizeEvent( QResizeEvent *e ); void paintEvent ( QPaintEvent * event ); public slots: void getSelectedItems(); void relationSet(int relation); void drawNode(const int &num, const int &nodeSize, const QString &nodeShape, const QString &nodeColor, const bool &showNumbers, const bool &numberInsideNode, const QString &numberColor, const int &numberSize, const int &numberDistance, const bool &showLabels, const QString &nodeLabel, const QString &labelColor, const int &labelSize, const int &labelDistance, const QPointF &p ); void eraseNode(const long int &number); void setNodeVisibility(long int, bool ); //Called from Graph via MW void nodeClicked(Node *); void moveNode(const int &num, const qreal &x, const qreal &y); //Called from Graph when creating random nets. bool setNodeSize(const long int &nodeNumber, const int &size=0); void setAllNodeSize(const int &size=0); bool setNodeShape(const long int &nodeNumber, const QString &shape); bool setNodeColor(const long int &, const QString &color); void setNodeNumberVisibility(const bool &toggle); bool setNodeNumberSize(const long int &, const int &size=0); bool setNodeNumberDistance(const long int &, const int &distance=0); void setNodeLabelsVisibility(const bool &toggle); bool setNodeLabelColor(const long int &number, const QString &color="green"); bool setNodeLabelSize(const long int &, const int &size=0); bool setNodeLabel(long int , QString ); bool setNodeLabelDistance(const long int &, const int &distance=0); void drawEdge(const int &source, const int &target, const float &weight, const QString &label="", const QString &color="black", const int &type=0, const bool &drawArrows=true, const bool &bezier=false, const bool &weightNumbers=false); void eraseEdge(const long int &source, const long int &target); void setEdgeVisibility (int relation, int, int, bool); bool setEdgeUndirected(const long int &, const long int &, const float &); bool setEdgeWeight(const long int &, const long int &, const float &); void setEdgeLabel(const long int &, const long int&, const QString &); void setEdgeColor(const long int &, const long int&, const QString &); void edgeClicked(Edge *); void setEdgeWeightNumbersVisibility (const bool &toggle); void setEdgeLabelsVisibility(const bool &toggle); void startEdge(Node *node); void clearGuides(); void addGuideCircle( const double&x0, const double&y0, const double&radius); void addGuideHLine(const double &y0); void zoomIn(int level = 1); void zoomOut(int level = 1); void rotateLeft(); void rotateRight(); void changeMatrixScale(const int value); void changeMatrixRotation(int angle); void reset(); signals: void userDoubleClickNewNode(const QPointF &); void userMiddleClicked(const int &, const int &); void userClickOnEmptySpace(const QPointF &p); void openNodeMenu(); void openEdgeMenu(); void openContextMenu(const QPointF p); void userNodeMoved(const int &, const int &, const int &); //void userSelectedItems(const int nodes, const int edges); void userSelectedItems(const QList &selectedNodes, const QList &selectedEdges); void userClickedNode(const int &nodeNumber); void userClickedEdge(const int &source, const int &target); void zoomChanged(const int); void rotationChanged(const int); void resized(const int, const int); void setCursor(Qt::CursorShape); private: H_NumToNode nodeHash; //This is used in drawEdge() method H_StrToEdge edgesHash; // helper hash to easily find edges QList m_selectedNodes; QList m_selectedEdges; int m_curRelation, m_nodeSize; int m_currentRotationAngle; int m_zoomIndex, markedNodeOrigSize,markedEdgeSourceOrigSize, markedEdgeTargetOrigSize; double m_currentScaleFactor; qreal fX,fY, factor; QString m_nodeLabel, m_numberColor, m_labelColor; QString edgeName; bool transformationActive; bool secondDoubleClick, markedNodeExist, clickedEdgeExists; Node *firstNode, *secondNode, *markedNode1, *markedEdgeSource; Node *markedEdgeTarget, *tempNode ; }; #endif socnetv-2.2/src/guide.cpp000755 000765 000024 00000006535 13040734201 016423 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt Guide.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include "guide.h" #include "graphicswidget.h" Guide::Guide ( GraphicsWidget *gw, const double &x0, const double &y0, const double &radius ) : graphicsWidget ( gw ){ graphicsWidget->scene()->addItem ( this ); m_radius=radius; setZValue(ZValueGuide); circle=true; setPos(x0, y0); } Guide::Guide ( GraphicsWidget *gw, const double &y0, const int &width) : graphicsWidget ( gw ){ graphicsWidget->scene()->addItem ( this ); setPos(0, y0); m_width= width; setZValue(ZValueGuide); circle=false; } double Guide::radius() { return m_radius; } bool Guide::isCircle() { return (circle); } void Guide::setCircle(const QPointF ¢er, const double &radius ) { setPos(center); m_radius=radius; circle = true; update(); } void Guide::setHorizontalLine(const QPointF &origin, const int &width){ setPos(origin); m_width= width; circle=false; update(); } int Guide::width() { return m_width; } /** Returns the bounding rectangle of the background circle*/ QRectF Guide::boundingRect() const { if (circle) { return QRectF ( - m_radius-1, - m_radius-1, + 2 * m_radius + 1, + 2* m_radius +1 ); } else { return QRectF ( 1, -1, m_width, + 1 ); } } void Guide::paint ( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * ){ Q_UNUSED(option); painter->setPen ( QPen ( QColor ( "red" ), 1, Qt::DotLine ) ); if (circle) { painter->drawEllipse ( QPointF(0,0), m_radius, m_radius ); } else { painter->drawLine ( 0 , 0, m_width , 0); } } void Guide::die (){ this->prepareGeometryChange(); this->hide(); this->update(); graphicsWidget->scene()->removeItem(this); this->update(); } socnetv-2.2/src/guide.h000755 000765 000024 00000005003 13040734202 016056 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt Guide.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef Guide_H #define Guide_H #include #include class GraphicsWidget; static const int TypeGuide = QGraphicsItem::UserType+7; static const int ZValueGuide = 10; class Guide : public QObject, public QGraphicsItem { Q_OBJECT Q_INTERFACES (QGraphicsItem) public: Guide(GraphicsWidget *, const double &x0, const double &y0, const double &radius ); Guide(GraphicsWidget *, const double &y0, const int &width); bool isCircle(); void setCircle(const QPointF ¢er, const double &radius) ; void setHorizontalLine(const QPointF &origin, const int &width) ; double radius(); int width(); enum { Type = UserType + 7 }; int type() const { return Type; } void die(); protected: QRectF boundingRect() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); private: GraphicsWidget *graphicsWidget; double m_radius; int m_width; bool circle; }; #endif socnetv-2.2/src/icon.rc000755 000765 000024 00000000061 13040701535 016070 0ustar00dimitrisstaff000000 000000 IDI_ICON1 ICON DISCARDABLE "images/socnetv.ico" socnetv-2.2/src/images/000755 000765 000024 00000000000 13040701535 016057 5ustar00dimitrisstaff000000 000000 socnetv-2.2/src/main.cpp000755 000765 000024 00000006464 13040734201 016253 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt main.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include //core Qt functionality #include //for text translations #include #include //used for cout #include "mainwindow.h" //main application window using namespace std; int main(int argc, char *argv[]) { Q_INIT_RESOURCE(src); QApplication app(argc, argv); QTranslator tor( 0 ); QLocale locale; // set the location where your .qm files are in load() below as the last parameter instead of "." // for development, use "/" to use the english original as // .qm files are stored in the base project directory. tor.load( QString("socnetv.") + locale.name(), "." ); app.installTranslator( &tor ); //Check if a filename is passed when this program is called. QString option; if ( argc > 1 ) { option = argv[1]; if (option=="--help" || option=="-h" || option=="--h" || option=="-help" ) { cout<<"\nSocial Network Visualizer v." << qPrintable(VERSION)<< "\n" <<"\nUsage: socnetv [flags] [file]\n" <<"-h, --help Displays this help message\n" <<"-V, --version Displays version number\n\n" <<"You can load a network from a file using \n" <<"socnetv file.net \n" <<"where file.net/csv/dot/graphml must be of valid format. See README\n\n" <<"Please send any bug reports to dimitris.kalamaras@gmail.com.\n\n"; return -1; } else if (option=="-V" || option=="--version") { cout<<"\nSocial Network Visualizer v." << qPrintable(VERSION) << "\nCopyright Dimitris V. Kalamaras, \nLicense: GPL3\n\n"; return -1; } else { cout<<"\nSocial Network Visualizer v." << qPrintable(VERSION); cout<<"\nLoading file: " << qPrintable(option) << "\n\n"; } } MainWindow *socnetv=new MainWindow(option); socnetv->show(); return app.exec(); } socnetv-2.2/src/mainwindow.cpp000755 000765 000024 00001766203 13040734202 017511 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt - mainwindow.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras blog : http://dimitris.apeiro.gr project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "mainwindow.h" #include "graphicswidget.h" #include "node.h" #include "edge.h" #include "nodenumber.h" #include "nodelabel.h" #include "edgeweight.h" #include "texteditor.h" #include "dialogfilteredgesbyweight.h" #include "guide.h" #include "vertex.h" #include "dialogpreviewfile.h" #include "dialogranderdosrenyi.h" #include "dialograndsmallworld.h" #include "dialograndscalefree.h" #include "dialograndregular.h" #include "dialogsettings.h" #include "dialogsimilaritypearson.h" #include "dialogsimilaritymatches.h" #include "dialogclusteringhierarchical.h" #include "dialogdissimilarities.h" bool printDebug = false; void myMessageOutput ( QtMsgType type, const QMessageLogContext &context, const QString &msg) { QByteArray localMsg = msg.toLocal8Bit(); Q_UNUSED(context); if ( printDebug ) switch ( type ) { case QtDebugMsg: fprintf( stderr, "Debug: %s\n", localMsg.constData() ); break; #if QT_VERSION >= 0x050500 case QtInfoMsg: fprintf( stderr, "Info: %s\n", localMsg.constData() ); break; #endif case QtWarningMsg: fprintf( stderr, "Warning: %s\n", localMsg.constData() ); break; case QtFatalMsg: fprintf( stderr, "Fatal: %s\n", localMsg.constData() ); abort(); // deliberately core dump case QtCriticalMsg: fprintf( stderr, "Critical: %s\n", localMsg.constData() ); abort(); // deliberately core dump } } /** * @brief MainWindow::MainWindow * @param m_fileName * MainWindow contruction method */ MainWindow::MainWindow(const QString & m_fileName) { appSettings = initSettings(); qInstallMessageHandler( myMessageOutput); setWindowIcon (QIcon(":/images/socnetv.png")); this->setMinimumSize(1024,750); //set MW minimum size, before creating canvas initView(); //init our network "canvas" /** functions that invoke all other construction parts **/ initActions(); //register and construct menu Actions initMenuBar(); //construct the menu initToolBar(); //build the toolbar initToolBox(); //build the toolbox initWindowLayout(); //init the application window, set layout etc initSignalSlots(); //connect signals and slots between app components initApp(); // load and initialise default app parameters // Check if user-provided network file on startup qDebug() << "MW::MainWindow() Checking if user provided file on startup..."; if (!m_fileName.isEmpty()) { slotNetworkFileChoose( m_fileName ); } graphicsWidget->setFocus(); statusMessage( tr("Welcome to Social Network Visualizer, Version ")+VERSION); } /** * @brief Deletes variables on MW closing */ MainWindow::~MainWindow() { qDebug() << "MW::~MainWindow() Destruct function running..."; initApp(); delete printer; delete scene; delete graphicsWidget; foreach ( TextEditor *ed, m_textEditors) { ed->close(); delete ed; } m_textEditors.clear(); qDebug() << "MW::~MainWindow() Destruct function finished - bye!"; } /** * @brief MainWindow::initSettings() * Init default (or user-defined) app settings * */ QMap MainWindow::initSettings(){ qDebug()<< "MW::initSettings"; printDebug = false; // comment it to stop debug override // Create fortune cookies and tips createFortuneCookies(); slotHelpCreateTips(); // Call slotNetworkAvailableTextCodecs to setup a list of all supported codecs qDebug() << "MW::initSettings - calling slotNetworkAvailableTextCodecs" ; slotNetworkAvailableTextCodecs(); qDebug() << "MW::initSettings - creating DialogPreviewFile object and setting codecs list" ; m_dialogPreviewFile = new DialogPreviewFile(this); m_dialogPreviewFile->setCodecList(codecs); connect (m_dialogPreviewFile, &DialogPreviewFile::loadNetworkFileWithCodec, this, &MainWindow::slotNetworkFileLoad ); qDebug() << "MW::initSettings - creating default settings" ; settingsDir = QDir::homePath() +QDir::separator() + "socnetv-data" + QDir::separator() ; settingsFilePath = settingsDir + "settings.conf"; // initially they are the same, but dataDir may be changed by the user QString dataDir= settingsDir ; maxNodes=5000; //Max nodes used by createRandomNetwork dialogues // hard-coded initial settings to use only on first app load // when there are no user defined values appSettings["initNodeSize"]= "10"; appSettings["initNodeColor"]="red"; appSettings["initNodeShape"]="circle"; appSettings["initNodeNumbersVisibility"] = "true"; appSettings["initNodeNumberSize"]="0"; appSettings["initNodeNumberColor"]="#333"; appSettings["initNodeNumbersInside"] = "true"; appSettings["initNodeNumberDistance"] = "2"; appSettings["initNodeLabelsVisibility"] = "false"; appSettings["initNodeLabelSize"]="6"; appSettings["initNodeLabelColor"]="#00aa00"; appSettings["initNodeLabelDistance"] = "6"; appSettings["initEdgesVisibility"]="true"; appSettings["initEdgeShape"]="line"; //bezier appSettings["initEdgeColor"]="black"; appSettings["initEdgeColorNegative"]="red"; appSettings["initEdgeArrows"]="true"; appSettings["initEdgeThicknessPerWeight"]="true"; appSettings["initEdgeWeightNumbersVisibility"]="false"; appSettings["initEdgeWeightNumberSize"] = "7"; appSettings["initEdgeWeightNumberColor"] = "#00aa00"; appSettings["initEdgeLabelsVisibility"] = "false"; appSettings["considerWeights"]="false"; appSettings["inverseWeights"]="false"; appSettings["askedAboutWeights"]="false"; appSettings["initBackgroundColor"]="white"; //"gainsboro"; appSettings["initBackgroundImage"]=""; appSettings["printDebug"] = (printDebug) ? "true" : "false"; appSettings["viewReportsInSystemBrowser"] = "true"; appSettings["showProgressBar"] = "true"; appSettings["showToolBar"] = "true"; appSettings["showStatusBar"] = "true"; appSettings["antialiasing"] = "true"; appSettings["dataDir"]= dataDir ; appSettings["lastUsedDirPath"]= dataDir ; appSettings["showRightPanel"] = "true"; appSettings["showLeftPanel"] = "true"; appSettings["printLogo"] = "true"; appSettings["initStatusBarDuration"] = "5000"; appSettings["randomErdosEdgeProbability"] = "0.04"; // Try to load settings configuration file // First check if our settings folder exist QDir socnetvDir(settingsDir); if ( !socnetvDir.exists() ) { qDebug() << "MW::initSettings - dir does not exist - create it"; socnetvDir.mkdir(settingsDir); } // Then check if the conf file exists inside the folder qDebug () << "MW::initSettings - checking for settings file: " << settingsFilePath; if (!socnetvDir.exists(settingsFilePath)) { saveSettings(); } else { qDebug()<< "MW::initSettings - settings file exist - Reading it"; QFile file(settingsFilePath); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { QMessageBox::critical(this, "File Read Error", tr("Error! \n" "I cannot read the settings file " "in \n" + settingsFilePath.toLocal8Bit() + "\n" "You can continue using SocNetV with default " "settings but any changes to them will not " " be saved for future sessions \n" "Please, check permissions in your home folder " " and conduct the developer." ), QMessageBox::Ok, 0); return appSettings; } QTextStream in(&file); QStringList setting; while (!in.atEnd()) { QString line = in.readLine(); if (!line.isEmpty()) { setting = line.simplified().split('='); qDebug() << " read setting: " << setting[0].simplified() << " = " << setting[1].simplified(); if (setting[0].simplified().startsWith("recentFile_")) recentFiles += setting[1].simplified(); else appSettings.insert (setting[0].simplified() , setting[1].simplified() ); } } file.close(); } qDebug () << "MW::initSettings() - Recent files count " << recentFiles.count() ; // restore user setting for debug messages printDebug = (appSettings["printDebug"] == "true") ? true:false; return appSettings; } /** * @brief MainWindow::saveSettings * Saves default (or user-defined) app settings */ void MainWindow::saveSettings() { qDebug () << "MW::saveSettings to "<< settingsFilePath; QFile file(settingsFilePath); // application settings file does not exist - create it // this must be the first time SocNetV runs in this computer // or the user might have deleted seetings file. if (!file.open(QIODevice::WriteOnly ) ) { QMessageBox::critical(this, "File Write Error", tr("Error! \n" "I cannot write the new settings file " "in \n" + settingsFilePath.toLocal8Bit() + "\n" "You can continue using SocNetV with default " "settings but any changes to them will not " " be saved for future sessions \n" "Please, check permissions in your home folder " " and conduct the developer." ), QMessageBox::Ok, 0); return; } qDebug()<< "MW::saveSettings - settings file does not exist - Creating it"; QTextStream out(&file); qDebug()<< "MW::saveSettings - writing settings to settings file first "; QMap::const_iterator it = appSettings.constBegin(); while (it != appSettings.constEnd()) { qDebug() << " setting: " << it.key() << " = " << it.value(); out << it.key() << " = " << it.value() << endl; ++it; } // save recent files for (int i = 0 ; i < recentFiles.size() ; ++i) { out << "recentFile_"+ QString::number(i+1) << " = " << recentFiles.at(i) << endl; } file.close(); } /** * @brief MainWindow::slotOpenSettingsDialog * Open Settings dialog */ void MainWindow::slotOpenSettingsDialog() { qDebug() << "MW;:slotOpenSettingsDialog()"; // build dialog m_settingsDialog = new DialogSettings( appSettings, this); connect( m_settingsDialog, &DialogSettings::saveSettings, this, &MainWindow::saveSettings); connect( m_settingsDialog, &DialogSettings::setDebugMsgs, this, &MainWindow::slotOptionsDebugMessages); connect( m_settingsDialog, &DialogSettings::setProgressBars, this, &MainWindow::slotOptionsProgressBarVisibility); connect( m_settingsDialog, &DialogSettings::setAntialiasing, this, &MainWindow::slotOptionsAntialiasing); connect( m_settingsDialog, &DialogSettings::setPrintLogo, this, &MainWindow::slotOptionsEmbedLogoExporting); connect( m_settingsDialog, &DialogSettings::setBgColor, this, &MainWindow::slotOptionsBackgroundColor); connect( m_settingsDialog, &DialogSettings::setBgImage, this, &MainWindow::slotOptionsBackgroundImage); connect( m_settingsDialog, &DialogSettings::setToolBar, this, &MainWindow::slotOptionsToolbarVisibility); connect( m_settingsDialog, &DialogSettings::setStatusBar, this, &MainWindow::slotOptionsStatusBarVisibility); connect( m_settingsDialog, &DialogSettings::setLeftPanel, this, &MainWindow::slotOptionsLeftPanelVisibility); connect( m_settingsDialog, &DialogSettings::setRightPanel, this, &MainWindow::slotOptionsRightPanelVisibility); connect(m_settingsDialog, SIGNAL(setNodeColor(QColor)), this, SLOT(slotEditNodeColorAll(QColor)) ); connect( m_settingsDialog, &DialogSettings::setNodeShape, this, &MainWindow::slotEditNodeShape); connect( m_settingsDialog, &DialogSettings::setNodeSize, this, &MainWindow::slotEditNodeSizeAll); connect( m_settingsDialog, &DialogSettings::setNodeNumbersVisibility, this, &MainWindow::slotOptionsNodeNumbersVisibility); connect( m_settingsDialog, &DialogSettings::setNodeNumbersInside, this, &MainWindow::slotOptionsNodeNumbersInside); connect( m_settingsDialog, &DialogSettings::setNodeNumberColor, this, &MainWindow::slotEditNodeNumbersColor); connect( m_settingsDialog, &DialogSettings::setNodeNumberSize, this, &MainWindow::slotEditNodeNumberSize); connect( m_settingsDialog, &DialogSettings::setNodeNumberDistance, this, &MainWindow::slotEditNodeNumberDistance); connect( m_settingsDialog, &DialogSettings::setNodeLabelsVisibility, this, &MainWindow::slotOptionsNodeLabelsVisibility); connect( m_settingsDialog, &DialogSettings::setNodeLabelSize, this, &MainWindow::slotEditNodeLabelSize); connect( m_settingsDialog, &DialogSettings::setNodeLabelColor, this, &MainWindow::slotEditNodeLabelsColor); connect( m_settingsDialog, &DialogSettings::setNodeLabelDistance, this, &MainWindow::slotEditNodeLabelDistance); connect( m_settingsDialog, &DialogSettings::setEdgesVisibility, this, &MainWindow::slotOptionsEdgesVisibility); connect( m_settingsDialog, &DialogSettings::setEdgeArrowsVisibility, this, &MainWindow::slotOptionsEdgeArrowsVisibility); connect( m_settingsDialog, &DialogSettings::setEdgeColor, this, &MainWindow::slotEditEdgeColorAll); connect( m_settingsDialog, &DialogSettings::setEdgeWeightNumbersVisibility, this, &MainWindow::slotOptionsEdgeWeightNumbersVisibility); connect( m_settingsDialog, &DialogSettings::setEdgeLabelsVisibility, this, &MainWindow::slotOptionsEdgeLabelsVisibility); // show settings dialog m_settingsDialog->exec(); qDebug ()<< appSettings["initBackgroundImage"] ; } /** * @brief MainWindow::initActions * Initializes ALL QActions of the application * Take a breath, the listing below is HUGE. */ void MainWindow::initActions(){ printer = new QPrinter; /** Network menu actions */ networkNew = new QAction(QIcon(":/images/new.png"), tr("&New"), this); networkNew->setShortcut(Qt::CTRL+Qt::Key_N); networkNew->setStatusTip(tr("Create a new network")); networkNew->setToolTip(tr("New network")); networkNew->setWhatsThis(tr("New\n\n" "Creates a new social network. " "Firtst, checks if current network needs to be saved.")); connect(networkNew, SIGNAL(triggered()), this, SLOT(slotNetworkNew())); networkOpen = new QAction(QIcon(":/images/open.png"), tr("&Open"), this); networkOpen->setShortcut(Qt::CTRL+Qt::Key_O); networkOpen->setToolTip(tr("Open network")); networkOpen->setStatusTip(tr("Open a GraphML formatted file of social network data.")); networkOpen->setWhatsThis(tr("Open\n\n" "Opens a file of a social network in GraphML format")); connect(networkOpen, SIGNAL(triggered()), this, SLOT(slotNetworkFileChoose())); for (int i = 0; i < MaxRecentFiles; ++i) { recentFileActs[i] = new QAction(this); recentFileActs[i]->setVisible(false); connect(recentFileActs[i], SIGNAL(triggered()), this, SLOT(slotNetworkFileLoadRecent())); } networkImportGML = new QAction( QIcon(":/images/open.png"), tr("&GML"), this); networkImportGML->setStatusTip(tr("Import GML-formatted file")); networkImportGML->setWhatsThis(tr("Import GML\n\n" "Imports a social network from a GML-formatted file")); connect(networkImportGML, SIGNAL(triggered()), this, SLOT(slotNetworkImportGML())); networkImportPajek = new QAction( QIcon(":/images/open.png"), tr("&Pajek"), this); networkImportPajek->setStatusTip(tr("Import Pajek-formatted file")); networkImportPajek->setWhatsThis(tr("Import Pajek \n\n" "Imports a social network from a Pajek-formatted file")); connect(networkImportPajek, SIGNAL(triggered()), this, SLOT(slotNetworkImportPajek())); networkImportSM = new QAction( QIcon(":/images/open.png"), tr("&Adjacency Matrix"), this); networkImportSM->setStatusTip(tr("Import Adjacency matrix")); networkImportSM->setWhatsThis(tr("Import Sociomatrix \n\n" "Imports a social network from an Adjacency matrix-formatted file")); connect(networkImportSM, SIGNAL(triggered()), this, SLOT(slotNetworkImportSM())); networkImportDot = new QAction( QIcon(":/images/open.png"), tr("GraphViz (.dot)"), this); networkImportDot->setStatusTip(tr("Import dot file")); networkImportDot->setWhatsThis(tr("Import GraphViz \n\n" "Imports a social network from an GraphViz formatted file")); connect(networkImportDot, SIGNAL(triggered()), this, SLOT(slotNetworkImportDot())); networkImportDL = new QAction( QIcon(":/images/open.png"), tr("UCINET (.dl)..."), this); networkImportDL->setStatusTip(tr("ImportDL-formatted file (UCINET)")); networkImportDL->setWhatsThis(tr("Import UCINET\n\n" "Imports social network data from a DL-formatted file")); connect(networkImportDL, SIGNAL(triggered()), this, SLOT(slotNetworkImportDL())); networkImportList = new QAction( QIcon(":/images/open.png"), tr("&Edge list"), this); networkImportList->setStatusTip(tr("Import an edge list file. ")); networkImportList->setWhatsThis( tr("Import edge list\n\n" "Import a network from an edgelist file. " "SocNetV supports EdgeList files with edge weights " "as well as simple EdgeList files where the edges are non-value (see manual)" )); connect(networkImportList, SIGNAL(triggered()), this, SLOT(slotNetworkImportEdgeList())); networkImportTwoModeSM = new QAction( QIcon(":/images/open.png"), tr("&Two Mode Sociomatrix"), this); networkImportTwoModeSM->setStatusTip(tr("Import two-mode sociomatrix (affiliation network) file")); networkImportTwoModeSM->setWhatsThis(tr("Import Two-Mode Sociomatrix \n\n" "Imports a two-mode network from a sociomatrix file. " "Two-mode networks are described by affiliation " "network matrices, where A(i,j) codes the " "events/organizations each actor is affiliated with.")); connect(networkImportTwoModeSM, SIGNAL(triggered()), this, SLOT(slotNetworkImportTwoModeSM())); networkSave = new QAction(QIcon(":/images/save.png"), tr("&Save"), this); networkSave->setShortcut(Qt::CTRL+Qt::Key_S); networkSave->setStatusTip(tr("Save social network to a file")); networkSave->setWhatsThis(tr("Save.\n\n" "Saves the social network to file")); connect(networkSave, SIGNAL(triggered()), this, SLOT(slotNetworkSave())); networkSaveAs = new QAction(QIcon(":/images/save.png"), tr("Save &As..."), this); networkSaveAs->setShortcut(Qt::CTRL+Qt::SHIFT+Qt::Key_S); networkSaveAs->setStatusTip(tr("Save network under a new filename")); networkSaveAs->setWhatsThis(tr("Save As\n\n" "Saves the social network under a new filename")); connect(networkSaveAs, SIGNAL(triggered()), this, SLOT(slotNetworkSaveAs())); networkExportBMP = new QAction(QIcon(":/images/image.png"), tr("&BMP..."), this); networkExportBMP->setStatusTip(tr("Export social network to BMP image")); networkExportBMP->setWhatsThis(tr("Export BMP\n\n" "Exports the social network to a BMP image")); connect(networkExportBMP, SIGNAL(triggered()), this, SLOT(slotNetworkExportBMP())); networkExportPNG = new QAction( QIcon(":/images/image.png"), tr("&PNG..."), this); networkExportPNG->setStatusTip(tr("Export social network to PNG image")); networkExportPNG->setWhatsThis(tr("Export PNG \n\n" "Exports the social network to a PNG image")); connect(networkExportPNG, SIGNAL(triggered()), this, SLOT(slotNetworkExportPNG())); networkExportPDF = new QAction( QIcon(":/images/pdf.png"), tr("&PDF..."), this); networkExportPDF->setStatusTip(tr("Export social network to PDF")); networkExportPDF->setWhatsThis(tr("Export PDF\n\n" "Exports the social network to a PDF document")); connect(networkExportPDF, SIGNAL(triggered()), this, SLOT(slotNetworkExportPDF())); networkExportSM = new QAction( QIcon(":/images/save.png"), tr("&Adjacency Matrix"), this); networkExportSM->setStatusTip(tr("Export social network to an adjacency/sociomatrix file")); networkExportSM->setWhatsThis(tr("Export network to Adjacency format\n\n" "Exports the social network to an " "adjacency matrix-formatted file")); connect(networkExportSM, SIGNAL(triggered()), this, SLOT(slotNetworkExportSM())); networkExportPajek = new QAction( QIcon(":/images/save.png"), tr("&Pajek"), this); networkExportPajek->setStatusTip(tr("Export social network to a Pajek-formatted file")); networkExportPajek->setWhatsThis(tr("Export Pajek \n\n" "Exports the social network to a Pajek-formatted file")); connect(networkExportPajek, SIGNAL(triggered()), this, SLOT(slotNetworkExportPajek())); networkExportList = new QAction( QIcon(":/images/save.png"), tr("&List"), this); networkExportList->setStatusTip(tr("Export to List-formatted file. ")); networkExportList->setWhatsThis(tr("Export List\n\n" "Exports the network to a List-formatted file")); connect(networkExportList, SIGNAL(triggered()), this, SLOT(slotNetworkExportList())); networkExportDL = new QAction( QIcon(":/images/save.png"), tr("&DL..."), this); networkExportDL->setStatusTip(tr("Export network to UCINET-formatted file")); networkExportDL->setWhatsThis(tr("Export UCINET\n\n" "Exports the active network to a DL-formatted")); connect(networkExportDL, SIGNAL(triggered()), this, SLOT(slotNetworkExportDL())); networkExportGW = new QAction( QIcon(":/images/save.png"), tr("&GW..."), this); networkExportGW->setStatusTip(tr("Export to GW-formatted file")); networkExportGW->setWhatsThis(tr("Export\n\n" "Exports the active network to a GW formatted file")); connect(networkExportGW, SIGNAL(triggered()), this, SLOT(slotNetworkExportGW())); networkClose = new QAction( tr("&Close"), this); networkClose->setStatusTip(tr("Close the actual network")); networkClose->setWhatsThis(tr("Close \n\nCloses the actual network")); connect(networkClose, SIGNAL(triggered()), this, SLOT(slotNetworkClose())); networkPrint = new QAction(QIcon(":/images/print.png"), tr("&Print"), this); networkPrint->setShortcut(Qt::CTRL+Qt::Key_P); networkPrint->setStatusTip(tr("Send the currrent social network to the printer")); networkPrint->setWhatsThis(tr("Print \n\n" "Sends whatever is viewable on " "the canvas to your printer. \n" "To print the whole social network, " "you might want to zoom-out.")); connect(networkPrint, SIGNAL(triggered()), this, SLOT(slotNetworkPrint())); networkQuit = new QAction(QIcon(":/images/exit.png"), tr("E&xit"), this); networkQuit->setShortcut(Qt::CTRL+Qt::Key_Q); networkQuit->setStatusTip(tr("Quit SocNetV. Are you sure?")); networkQuit->setWhatsThis(tr("Exit\n\n" "Quits the application")); connect(networkQuit, SIGNAL(triggered()), this, SLOT(close())); openTextEditorAct = new QAction(QIcon(":/images/texteditor.png"), tr("Open Text Editor"),this); openTextEditorAct ->setShortcut(Qt::SHIFT+Qt::Key_F5); openTextEditorAct->setStatusTip(tr("Open a text editor " "to take notes, copy/paste network data, etc")); openTextEditorAct->setWhatsThis(tr("Text Editor\n\n" "Opens a simple text editor where you can " "copy paste network data, of any supported format, " "and save to a file. Then you can import that file to SocNetV...")); connect(openTextEditorAct, SIGNAL(triggered()), this, SLOT(slotNetworkTextEditor())); networkViewFileAct = new QAction(QIcon(":/images/networkfile.png"), tr("View Loaded File"),this); networkViewFileAct ->setShortcut(Qt::Key_F5); networkViewFileAct->setStatusTip(tr("Display the loaded social network file.")); networkViewFileAct->setWhatsThis(tr("View Loaded File\n\n" "Displays the loaded social network file ")); connect(networkViewFileAct, SIGNAL(triggered()), this, SLOT(slotNetworkFileView())); networkViewSociomatrixAct = new QAction(QIcon(":/images/sm.png"), tr("View Adjacency Matrix"), this); networkViewSociomatrixAct ->setShortcut(Qt::Key_F6); networkViewSociomatrixAct->setStatusTip(tr("Display the adjacency matrix of the network.")); networkViewSociomatrixAct->setWhatsThis(tr("View Adjacency Matrix\n\n" "Displays the adjacency matrix of the active network. \n\n" "The adjacency matrix of a social network is a matrix " "where each element a(i,j) is equal to the weight " "of the arc from actor (node) i to actor j. " "If the actors are not connected, then a(i,j)=0. ")); connect(networkViewSociomatrixAct, SIGNAL(triggered()), this, SLOT(slotNetworkViewSociomatrix())); networkViewSociomatrixPlotAct = new QAction(QIcon(":/images/adjacencyplot.png"), tr("Plot Adjacency Matrix (text)"), this); networkViewSociomatrixPlotAct ->setShortcut(Qt::SHIFT + Qt::Key_F6); networkViewSociomatrixPlotAct->setStatusTip(tr("Plots the adjacency matrix in a text file using unicode characters.")); networkViewSociomatrixPlotAct->setWhatsThis( tr("Plot Adjacency Matrix (text)\n\n" "Plots the adjacency matrix in a text file using " "unicode characters. \n\n" "In every element (i,j) of the \"image\", " "a black square means actors i and j are connected" "whereas a white square means they are disconnected." )); connect(networkViewSociomatrixPlotAct, SIGNAL(triggered()), this, SLOT(slotNetworkViewSociomatrixPlotText())); networkDataSetSelectAct = new QAction(QIcon(":/images/petersengraph.png"), tr("Create From Known Data Sets"), this); networkDataSetSelectAct ->setShortcut(Qt::Key_F7); networkDataSetSelectAct->setStatusTip(tr("Create a social network using one of the \'famous\' " "social network data sets included in SocNetV.")); networkDataSetSelectAct->setWhatsThis(tr("Known Data Sets\n\n" "SocNetV includes a number of known " "(also called famous) data sets in Social Network Analysis, " "such as Krackhardt's high-tech managers, etc. " "Click this menu item or press F7 to select a data set. " "")); connect(networkDataSetSelectAct, SIGNAL(triggered()), this, SLOT(slotNetworkDataSetSelect())); createErdosRenyiRandomNetworkAct = new QAction(QIcon(":/images/erdos.png"), tr("Erdős–Rényi"), this); createErdosRenyiRandomNetworkAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_E) ); createErdosRenyiRandomNetworkAct->setStatusTip(tr("Create a random network " "according to the Erdős–Rényi model")); createErdosRenyiRandomNetworkAct->setWhatsThis( tr("Erdős–Rényi \n\n" "Creates a random network either of G(n, p) model or G(n,M) model.\n" "In the first, edges are created with Bernoulli trials (probability p).\n" "In the second, a graph of exactly M edges is created.")); connect(createErdosRenyiRandomNetworkAct, SIGNAL(triggered()), this, SLOT(slotNetworkRandomErdosRenyiDialog())); createLatticeNetworkAct = new QAction( QIcon(":/images/net1.png"), tr("Ring Lattice"), this); createLatticeNetworkAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_L) ); createLatticeNetworkAct->setStatusTip(tr("Create a ring lattice random network.")); createLatticeNetworkAct->setWhatsThis( tr("Ring Lattice \n\n")+ tr("A ring lattice is a graph with N vertices each connected to d neighbors, d / 2 on each side.")); connect(createLatticeNetworkAct, SIGNAL(triggered()), this, SLOT(slotNetworkRandomRingLattice())); createRegularRandomNetworkAct = new QAction(QIcon(":/images/net.png"), tr("d-Regular"), this); createRegularRandomNetworkAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_R) ); createRegularRandomNetworkAct->setStatusTip( tr("Create a d-regular random network, where every actor has the same degree d.")); createRegularRandomNetworkAct->setWhatsThis( tr("d-Regular \n\n") + tr("A random network where each actor has the same " "number d of neighbours, aka the same degree d ")); connect(createRegularRandomNetworkAct, SIGNAL(triggered()), this, SLOT(slotNetworkRandomRegularDialog())); createGaussianRandomNetworkAct = new QAction(tr("Gaussian"), this); createGaussianRandomNetworkAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_G) ); createGaussianRandomNetworkAct->setStatusTip(tr("Create a Gaussian distributed random network.")); createGaussianRandomNetworkAct->setWhatsThis(tr("Gaussian \n\nCreates a random network of Gaussian distribution")); connect(createGaussianRandomNetworkAct, SIGNAL(triggered()), this, SLOT(slotNetworkRandomGaussian())); createSmallWorldRandomNetworkAct = new QAction(QIcon(":/images/sw.png"), tr("Small World"), this); createSmallWorldRandomNetworkAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_W) ); createSmallWorldRandomNetworkAct->setStatusTip(tr("Create a small-world random network.")); createSmallWorldRandomNetworkAct -> setWhatsThis( tr("Small World \n\n") + tr("A Small World, according to the Watts and Strogatz model, " "is a random network with short average path lengths and high clustering coefficient.")); connect(createSmallWorldRandomNetworkAct, SIGNAL(triggered()), this, SLOT(slotNetworkRandomSmallWorldDialog())); createScaleFreeRandomNetworkAct = new QAction( QIcon(":/images/scalefree.png"), tr("Scale-free"), this); createScaleFreeRandomNetworkAct->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_R, Qt::CTRL + Qt::Key_S) ); createScaleFreeRandomNetworkAct->setStatusTip( tr("Create a random network with power-law degree distribution.")); createScaleFreeRandomNetworkAct-> setWhatsThis( tr("Scale-free (power-law)\n\n") + tr("A scale-free network is a network whose degree distribution follows a power law." " SocNetV generates random scale-free networks according to the " " Barabási–Albert (BA) model using a preferential attachment mechanism.")); connect(createScaleFreeRandomNetworkAct, SIGNAL(triggered()), this, SLOT(slotNetworkRandomScaleFreeDialog())); webCrawlerAct = new QAction(QIcon(":/images/spider.png"), tr("Web Crawler"), this); webCrawlerAct->setShortcut(Qt::SHIFT+Qt::Key_C); webCrawlerAct->setEnabled(true); webCrawlerAct->setStatusTip(tr("Create a network from all links found in a given website" "Shift+C")); webCrawlerAct->setWhatsThis(tr("Web Crawler \n\n" "A Web crawler is a built-in bot, which " "starts with a given URL (website or webpage) " "to visit. As the algorithm crawls this webpage, " "it identifies all the links in the page and adds " "them to a list of URLs (called frontier). " "Then, all the URLs from the frontier are " "recursively visited. You must provide maximum " "recursion level (how many URLs from the frontier " "will be visited) and maximum running time, along " "with the initial web address...")); connect(webCrawlerAct, SIGNAL(triggered()), this, SLOT(slotNetworkWebCrawlerDialog())); /** Edit menu actions */ editRelationNextAct = new QAction(QIcon(":/images/nextrelation.png"), tr("Next Relation"), this); editRelationNextAct->setShortcut(Qt::ALT + Qt::Key_Right); editRelationNextAct->setToolTip(tr("Goto next graph relation (ALT+Right)")); editRelationNextAct->setStatusTip(tr("Load the next relation of the network (if any).")); editRelationNextAct->setWhatsThis(tr("Next Relation\n\nLoads the next relation of the network (if any)")); editRelationPreviousAct = new QAction(QIcon(":/images/prevrelation.png"), tr("Previous Relation"), this); editRelationPreviousAct->setShortcut(Qt::ALT + Qt::Key_Left); editRelationPreviousAct->setToolTip( tr("Goto previous graph relation (ALT+Left)")); editRelationPreviousAct->setStatusTip( tr("Load the previous relation of the network (if any).")); editRelationPreviousAct->setWhatsThis( tr("Previous Relation\n\n" "Loads the previous relation of the network (if any)")); editRelationAddAct = new QAction(QIcon(":/images/addrelation.png"), tr("Add New Relation"), this); editRelationAddAct->setShortcut(Qt::ALT + Qt::CTRL + Qt::Key_N); editRelationAddAct->setToolTip( tr("Add a new relation to the active graph (Ctrl+Shift+N)")); editRelationAddAct->setStatusTip( tr("Add a new relation to the network. " "Nodes will be preserved, edges will be removed. ")); editRelationAddAct->setWhatsThis( tr("Add New Relation\n\n" "Adds a new relation to the active network. " "Nodes will be preserved, edges will be removed. ")); editRelationRenameAct = new QAction(QIcon(":/images/edit-rename.png"), tr("Rename Relation"), this); editRelationRenameAct->setToolTip(tr("Rename current relation")); editRelationRenameAct->setStatusTip(tr("Rename the current relation of the network (if any).")); editRelationRenameAct->setWhatsThis(tr("Rename Relation\n\n" "Renames the current relation of the network (if any).")); zoomInAct = new QAction(QIcon(":/images/zoomin.png"), tr("Zoom In"), this); zoomInAct->setStatusTip(tr("Zoom in. Better, use the canvas button or press Ctrl++ or press Cltr and use mouse wheel.")); zoomInAct->setToolTip(tr("Zoom in. Better, use the canvas button or (Ctrl++)")); zoomInAct->setWhatsThis(tr("Zoom In.\n\nZooms in the actual network")); connect(zoomInAct, SIGNAL(triggered()), graphicsWidget, SLOT( zoomIn()) ); zoomOutAct = new QAction(QIcon(":/images/zoomout.png"), tr("Zoom Out"), this); zoomOutAct->setStatusTip(tr("Zoom out. Better, use the canvas button or press Ctrl+- or press Cltr and use mouse wheel.")); zoomOutAct->setToolTip(tr("Zoom in. Better, use the canvas button or (Ctrl+-)")); zoomOutAct->setWhatsThis(tr("Zoom Out.\n\nZooms out of the actual network")); connect(zoomOutAct, SIGNAL(triggered()), graphicsWidget, SLOT( zoomOut()) ); editRotateLeftAct = new QAction(QIcon(":/images/rotateleft.png"), tr("Rotate counterclockwise"), this); editRotateLeftAct->setToolTip(tr("Rotate counterclockwise. Better, use the canvas button or (Ctrl+Left Arrow)")); editRotateLeftAct->setStatusTip(tr("Rotate counterclockwise. Better, use the canvas button or Ctrl+Left Arrow")); editRotateLeftAct ->setWhatsThis(tr("Rotates the network counterclockwise (Ctrl+Left Arrow)")); connect(editRotateLeftAct, SIGNAL(triggered()), graphicsWidget, SLOT( rotateLeft()) ); editRotateRightAct = new QAction(QIcon(":/images/rotateright.png"), tr("Rotate clockwise"), this); editRotateRightAct->setStatusTip(tr("Rotate clockwise. Better, use the canvas button or (Ctrl+Right Arrow)")); editRotateRightAct->setToolTip(tr("Rotate clockwise. Better, use the canvas button or (Ctrl+Right Arrow)")); editRotateRightAct ->setWhatsThis(tr("Rotates the network clockwise (Ctrl+Right Arrow)")); connect(editRotateRightAct, SIGNAL(triggered()), graphicsWidget, SLOT( rotateRight()) ); editResetSlidersAct = new QAction(QIcon(":/images/reset.png"), tr("Reset Zoom and Rotation"), this); editResetSlidersAct->setStatusTip(tr("Reset zoom and rotation to zero (Ctrl+0)")); editResetSlidersAct->setToolTip(tr("Reset zoom and rotation to zero (Ctrl+0)")); editResetSlidersAct->setWhatsThis(tr("Reset zoom and rotation to zero (Ctrl+0)")); connect(editResetSlidersAct, SIGNAL(triggered()), graphicsWidget, SLOT( reset()) ); editNodeSelectAllAct = new QAction(QIcon(":/images/selectall.png"), tr("Select All"), this); editNodeSelectAllAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_A)); editNodeSelectAllAct->setStatusTip(tr("Select all nodes")); editNodeSelectAllAct->setWhatsThis(tr("Select All\n\nSelects all nodes in the network")); connect(editNodeSelectAllAct, SIGNAL(triggered()), this, SLOT(slotEditNodeSelectAll())); editNodeSelectNoneAct = new QAction(QIcon(":/images/selectnone.png"), tr("Deselect All"), this); editNodeSelectNoneAct->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_A)); editNodeSelectNoneAct->setStatusTip(tr("Deselect all nodes")); editNodeSelectNoneAct->setWhatsThis(tr("Deselect all\n\n Clears the node selection")); connect(editNodeSelectNoneAct, SIGNAL(triggered()), this, SLOT(slotEditNodeSelectNone())); editNodeFindAct = new QAction(QIcon(":/images/find.png"), tr("Find Node"), this); editNodeFindAct->setShortcut(Qt::CTRL + Qt::Key_F); editNodeFindAct->setToolTip(tr("Find an actor by its number or label and highlight it. " "Press Ctrl+F again to undo.")); editNodeFindAct->setStatusTip(tr("Find an actor by its number or label and highlight it. " "Press Ctrl+F again to undo.")); editNodeFindAct->setWhatsThis(tr("Find Node\n\n" "Finds a node with a given number or label and " "highlights it by doubling its size. " "Ctrl+F again resizes back the node")); connect(editNodeFindAct, SIGNAL(triggered()), this, SLOT(slotEditNodeFind()) ); editNodeAddAct = new QAction(QIcon(":/images/add.png"), tr("Add Node"), this); editNodeAddAct->setShortcut(Qt::CTRL + Qt::Key_Period); editNodeAddAct->setToolTip( tr("Add a new node to the network (Ctrl+.). \n\n" "You can also create a new node \n" "in a specific position by double-clicking.") ); editNodeAddAct->setWhatsThis( tr("Add new node\n\n" "Adds a new node to the network (Ctrl+.). \n\n" "Alternately, you can create a new node " "in a specific position by double-clicking " "on that spot of the canvas.") ); connect(editNodeAddAct, SIGNAL(triggered()), this, SLOT(slotEditNodeAdd())); editNodeRemoveAct = new QAction(QIcon(":/images/remove.png"),tr("Remove Node"), this); editNodeRemoveAct ->setShortcut(Qt::CTRL + Qt::ALT + Qt::Key_Period); //Single key shortcuts with backspace or del do no work in Mac http://goo.gl/7hz7Dx editNodeRemoveAct->setToolTip(tr("Remove selected node(s). \n\n" "If no nodes are selected, you will be prompted " "for a node number. ")); editNodeRemoveAct->setStatusTip(tr("Remove selected node(s). If no nodes are selected, " "you will be prompted for a node number. ")); editNodeRemoveAct->setWhatsThis( tr("Remove node\n\n" "Removes selected node(s) from the network (Ctrl+Alt+.). \n" "Alternately, you can remove a node by right-clicking on it. \n" "If no nodes are selected, you will be prompted for a node number. ") ); connect(editNodeRemoveAct, SIGNAL(triggered()), this, SLOT(slotEditNodeRemove())); editNodePropertiesAct = new QAction(QIcon(":/images/properties.png"),tr("Selected Node Properties"), this); editNodePropertiesAct ->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_Period ); editNodePropertiesAct->setToolTip(tr("Change the basic properties of the selected node(s) \n\n" "There must be some nodes on the canvas!")); editNodePropertiesAct->setStatusTip(tr("Change the basic properties of the selected node(s) -- " "There must be some nodes on the canvas!")); editNodePropertiesAct->setWhatsThis(tr("Selected Node Properties\n\n" "If there are some nodes on the canvas, " " opens a properties dialog to edit " "their label, size, color, shape etc. \n" "You must have some node selected.")); connect(editNodePropertiesAct, SIGNAL(triggered()), this, SLOT(slotEditNodePropertiesDialog())); editNodeSelectedToCliqueAct = new QAction(QIcon(":/images/cliquenew.png"), tr("Create a clique from selected nodes "), this); editNodeSelectedToCliqueAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_X, Qt::CTRL + Qt::Key_C)); editNodeSelectedToCliqueAct->setStatusTip(tr("Connect all selected nodes with edges to create a clique -- " "There must be some nodes selected!")); editNodeSelectedToCliqueAct->setWhatsThis(tr("Clique from Selected Nodes\n\n" "Adds all possible edges between selected nodes, " "so that they become a complete subgraph (clique)\n" "You must have some nodes selected.")); connect(editNodeSelectedToCliqueAct, SIGNAL(triggered()), this, SLOT(slotEditNodeSelectedToClique())); editNodeSelectedToStarAct = new QAction(QIcon(":/images/subgraphstar.png"), tr("Create a star from selected nodes "), this); editNodeSelectedToStarAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_X, Qt::CTRL + Qt::Key_S)); editNodeSelectedToStarAct->setStatusTip(tr("Connect selected nodes with edges/arcs to create a star -- " "There must be some nodes selected!")); editNodeSelectedToStarAct->setWhatsThis(tr("Star from Selected Nodes\n\n" "Adds edges between selected nodes, " "so that they become a star subgraph.\n" "You must have some nodes selected.")); connect(editNodeSelectedToStarAct, SIGNAL(triggered()), this, SLOT(slotEditNodeSelectedToStar())); editNodeSelectedToCycleAct = new QAction(QIcon(":/images/subgraphcycle.png"), tr("Create a cycle from selected nodes "), this); editNodeSelectedToCycleAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_X, Qt::CTRL + Qt::Key_Y)); editNodeSelectedToCycleAct->setStatusTip(tr("Connect selected nodes with edges/arcs to create a star -- " "There must be some nodes selected!")); editNodeSelectedToCycleAct->setWhatsThis(tr("Cycle from Selected Nodes\n\n" "Adds edges between selected nodes, " "so that they become a cycle subgraph.\n" "You must have some nodes selected.")); connect(editNodeSelectedToCycleAct, SIGNAL(triggered()), this, SLOT(slotEditNodeSelectedToCycle())); editNodeSelectedToLineAct = new QAction(QIcon(":/images/subgraphline.png"), tr("Create a line from selected nodes "), this); editNodeSelectedToLineAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_X, Qt::CTRL + Qt::Key_Y)); editNodeSelectedToLineAct->setStatusTip(tr("Connect selected nodes with edges/arcs to create a line-- " "There must be some nodes selected!")); editNodeSelectedToLineAct->setWhatsThis(tr("Line from Selected Nodes\n\n" "Adds edges between selected nodes, " "so that they become a line subgraph.\n" "You must have some nodes selected.")); connect(editNodeSelectedToLineAct, SIGNAL(triggered()), this, SLOT(slotEditNodeSelectedToLine())); editNodeColorAll = new QAction(QIcon(":/images/nodecolor.png"), tr("Change All Nodes Color (this session)"), this); editNodeColorAll->setStatusTip(tr("Choose a new color for all nodes (in this session only).")); editNodeColorAll->setWhatsThis(tr("Nodes Color\n\n" "Changes all nodes color at once. \n" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); connect(editNodeColorAll, SIGNAL(triggered()), this, SLOT(slotEditNodeColorAll()) ); editNodeSizeAllAct = new QAction(QIcon(":/images/resize.png"), tr("Change All Nodes Size (this session)"), this); editNodeSizeAllAct->setStatusTip(tr("Change the size of all nodes (in this session only)")); editNodeSizeAllAct->setWhatsThis(tr("Change All Nodes Size\n\n" "Click to select and apply a new size for all nodes at once. \n" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); connect(editNodeSizeAllAct, SIGNAL(triggered()), this, SLOT(slotEditNodeSizeAll()) ); editNodeShapeAll = new QAction(QIcon(":/images/nodeshape.png"), tr("Change All Nodes Shape (this session)"), this); editNodeShapeAll->setStatusTip(tr("Change the shape of all nodes (this session only)")); editNodeShapeAll->setWhatsThis(tr("Change All Nodes Shape\n\n" "Click to select and apply a new shape for all nodes at once." "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); connect(editNodeShapeAll, SIGNAL(triggered()), this, SLOT(slotEditNodeShape()) ); editNodeNumbersSizeAct = new QAction(QIcon(":/images/nodenumbersize.png"), tr("Change All Node Numbers Size (this session)"), this); editNodeNumbersSizeAct->setStatusTip(tr("Change the font size of the numbers of all nodes" "(in this session only)")); editNodeNumbersSizeAct->setWhatsThis(tr("Change Node Numbers Size\n\n" "Click to select and apply a new font size for all node numbers" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); connect(editNodeNumbersSizeAct, SIGNAL(triggered()), this, SLOT( slotEditNodeNumberSize( )) ); editNodeNumbersColorAct = new QAction(QIcon(":/images/nodenumbercolor.png"), tr("Change All Node Numbers Color (this session)"), this); editNodeNumbersColorAct->setStatusTip(tr("Change the color of the numbers of all nodes." "(in this session only)")); editNodeNumbersColorAct->setWhatsThis(tr("Node Numbers Color\n\n" "Click to select and apply a new color " "to all node numbers." "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); connect(editNodeNumbersColorAct, SIGNAL(triggered()), this, SLOT(slotEditNodeNumbersColor())); editNodeLabelsSizeAct = new QAction(QIcon(":/images/nodelabelsize.png"), tr("Change All Node Labels Size (this session)"), this); editNodeLabelsSizeAct->setStatusTip(tr("Change the font size of the labels of all nodes" "(this session only)")); editNodeLabelsSizeAct->setWhatsThis(tr("Node Labels Size\n\n" "Click to select and apply a new font-size to all node labels" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); connect(editNodeLabelsSizeAct, SIGNAL(triggered()), this, SLOT(slotEditNodeLabelSize()) ); editNodeLabelsColorAct = new QAction(QIcon(":/images/nodelabelcolor.png"), tr("Change All Node Labels Color (this session)"), this); editNodeLabelsColorAct->setStatusTip(tr("Change the color of the labels of all nodes " "(for this session only)")); editNodeLabelsColorAct->setWhatsThis(tr("Labels Color\n\n" "Click to select and apply a new color to all node labels." "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); connect(editNodeLabelsColorAct, SIGNAL(triggered()), this, SLOT(slotEditNodeLabelsColor())); editEdgeAddAct = new QAction(QIcon(":/images/connect.png"), tr("Add Edge (arc)"),this); editEdgeAddAct->setShortcut(Qt::CTRL + Qt::Key_Slash); editEdgeAddAct->setStatusTip(tr("Add a directed edge (arc) from a node to another")); editEdgeAddAct->setToolTip( tr("Add a new edge from a node to another (Ctrl+/).\n\n" "You can also create an edge between two nodes \n" "by double-clicking or middle-clicking on them consecutively.")); editEdgeAddAct->setWhatsThis( tr("Add edge\n\n" "Adds a new edge from a node to another (Ctrl+/).\n\n" "Alternately, you can create a new edge between two nodes " "by double-clicking or middle-clicking on them consecutively.") ); connect(editEdgeAddAct, SIGNAL(triggered()), this, SLOT(slotEditEdgeAdd())); editEdgeRemoveAct = new QAction(QIcon(":/images/disconnect.png"), tr("Remove Edge"), this); editEdgeRemoveAct ->setShortcut(Qt::CTRL + Qt::ALT + Qt::Key_Slash); editEdgeRemoveAct ->setToolTip(tr("Remove selected edges from the network (Ctrl+Alt+/). \n\n" "If no edge has been clicked or selected, you will be prompted \n" "to enter edge source and target nodes for the edge to remove.")); editEdgeRemoveAct->setStatusTip(tr("Remove selected Edge(s) (Ctrl+Alt+/)")); editEdgeRemoveAct->setWhatsThis(tr("Remove Edge\n\n" "Removes edges from the network (Ctrl+Alt+/). \n" "If one or more edges has been clicked or selected, they are removed. " "Otherwise, you will be prompted to enter edge source and target " "nodes for the edge to remove.")); connect(editEdgeRemoveAct, SIGNAL(triggered()), this, SLOT(slotEditEdgeRemove())); editEdgeLabelAct = new QAction(QIcon(":/images/letters.png"), tr("Change Edge Label"), this); editEdgeLabelAct->setStatusTip(tr("Change the Label of an Edge")); editEdgeLabelAct->setWhatsThis(tr("Change Edge Label\n\n" "Changes the label of an Edge")); connect(editEdgeLabelAct, SIGNAL(triggered()), this, SLOT(slotEditEdgeLabel())); editEdgeColorAct = new QAction(QIcon(":/images/colorize.png"),tr("Change Edge Color"), this); editEdgeColorAct->setStatusTip(tr("Change the Color of an Edge")); editEdgeColorAct->setWhatsThis(tr("Change Edge Color\n\n" "Changes the Color of an Edge")); connect(editEdgeColorAct, SIGNAL(triggered()), this, SLOT(slotEditEdgeColor())); editEdgeWeightAct = new QAction(QIcon(":/images/edgeweight.png") ,tr("Change Edge Weight"), this); editEdgeWeightAct->setStatusTip(tr("Change the weight of an Edge")); editEdgeWeightAct->setWhatsThis(tr("Edge Weight\n\n" "Changes the Weight of an Edge")); connect(editEdgeWeightAct, SIGNAL(triggered()), this, SLOT(slotEditEdgeWeight())); editEdgeColorAllAct = new QAction(QIcon(":/images/edgecolor.png"), tr("Change All Edges Color"), this); editEdgeColorAllAct->setStatusTip(tr("Change the color of all Edges.")); editEdgeColorAllAct->setWhatsThis(tr("All Edges Color\n\n" "Changes the color of all Edges")); connect(editEdgeColorAllAct, SIGNAL(triggered()), this, SLOT(slotEditEdgeColorAll())); editEdgeSymmetrizeAllAct= new QAction(QIcon(":/images/symmetrize.png"), tr("Symmetrize Directed Edges"), this); editEdgeSymmetrizeAllAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E, Qt::CTRL + Qt::Key_S)); editEdgeSymmetrizeAllAct->setStatusTip(tr("Make all arcs in this relation reciprocal (thus, a symmetric graph).")); editEdgeSymmetrizeAllAct->setWhatsThis( tr("Symmetrize Directed Edges\n\n" "Makes all directed arcs in this relation reciprocal. \n" "If there is an arc from node A to node B \n" "then a new arc from node B to node A is created \n" "with the same weight" "The result is a symmetric network")); connect(editEdgeSymmetrizeAllAct, SIGNAL(triggered()), this, SLOT(slotEditEdgeSymmetrizeAll())); editEdgeSymmetrizeStrongTiesAct= new QAction(QIcon(":/images/symmetrize.png"), tr("Symmetrize Edges by Strong Ties"), this); editEdgeSymmetrizeStrongTiesAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E, Qt::CTRL + Qt::Key_T)); editEdgeSymmetrizeStrongTiesAct->setStatusTip(tr("Create a new symmetric relation by counting reciprocated ties only (strong ties).")); editEdgeSymmetrizeStrongTiesAct->setWhatsThis( tr("Symmetrize Edges by examing Strong Ties\n\n" "Creates a new symmetric relation by keeping strong ties only. \n" "That is, a strong tie exists between actor A and actor B \n" "only when both arcs A -> B and B -> A are present. \n" "If the network is multi-relational, it asks you whether \n" "ties in the current relation or all relations are to be considered. \n" "The resulting relation is symmetric.")); connect(editEdgeSymmetrizeStrongTiesAct, SIGNAL(triggered()), this, SLOT(slotEditEdgeSymmetrizeStrongTies())); //TODO Separate action for Directed/Undirected graph drawing (without changing all existing edges). editEdgeUndirectedAllAct= new QAction( tr("Undirected Edges"), this); editEdgeUndirectedAllAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E, Qt::CTRL + Qt::Key_U)); editEdgeUndirectedAllAct->setStatusTip(tr("Enable to tranform all arcs to undirected edges and hereafter work with undirected edges .")); editEdgeUndirectedAllAct->setWhatsThis( tr("Undirected Edges\n\n" "Tranforms all directed arcs to undirected edges. \n" "The result is a undirected and symmetric network." "After that, every new edge you add, will be undirected too." "If you disable this, then all edges become directed again.")); editEdgeUndirectedAllAct -> setCheckable(true); editEdgeUndirectedAllAct -> setChecked(false); connect(editEdgeUndirectedAllAct, SIGNAL(triggered(bool)), this, SLOT(slotEditEdgeUndirectedAll(bool))); editEdgesCocitationAct= new QAction(QIcon(":/images/symmetrize.png"), tr("Cocitation Network"), this); editEdgesCocitationAct ->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E, Qt::CTRL + Qt::Key_C)); editEdgesCocitationAct->setStatusTip(tr("Create a new symmetric relation by " "connecting actors that are cocitated by others.")); editEdgesCocitationAct->setWhatsThis( tr("Symmetrize Edges by examing Strong Ties\n\n" "Create a new symmetric relation by connecting actors " "that are cocitated by others. \n" "In the new relation, an edge will exist between actor i and " "actor j only if C(i,j) > 0, where C the Cocitation Matrix. " "Thus the actor pairs cited by more common neighbors will appear " "with a stronger tie between them than pairs those cited by fewer " "common neighbors. " "The resulting relation is symmetric.")); connect(editEdgesCocitationAct, SIGNAL(triggered()), this, SLOT(slotEditEdgeSymmetrizeCocitation())); transformNodes2EdgesAct = new QAction( tr("Transform Nodes to Edges"),this); transformNodes2EdgesAct->setStatusTip(tr("Transforms the network so that " "nodes become Edges and vice versa")); transformNodes2EdgesAct->setWhatsThis(tr("Transform Nodes EdgesAct\n\n" "Transforms network so that nodes become Edges and vice versa")); connect(transformNodes2EdgesAct, SIGNAL(triggered()), this, SLOT(slotEditTransformNodes2Edges())); filterNodesAct = new QAction(tr("Filter Nodes"), this); filterNodesAct -> setEnabled(false); //filterNodesAct->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_X, Qt::CTRL + Qt::Key_F)); filterNodesAct->setStatusTip(tr("Filters Nodes of some value out of the network")); filterNodesAct->setWhatsThis(tr("Filter Nodes\n\n" "Filters Nodes of some value out of the network.")); connect(filterNodesAct, SIGNAL(triggered()), this, SLOT(slotFilterNodes())); editFilterNodesIsolatesAct = new QAction(tr("Disable Isolate Nodes"), this); editFilterNodesIsolatesAct -> setEnabled(true); editFilterNodesIsolatesAct -> setCheckable(true); editFilterNodesIsolatesAct -> setChecked(false); editFilterNodesIsolatesAct -> setShortcut(QKeySequence(Qt::CTRL + Qt::Key_X, Qt::CTRL + Qt::Key_F)); editFilterNodesIsolatesAct -> setStatusTip(tr("Temporarily filter out nodes with no edges")); editFilterNodesIsolatesAct -> setWhatsThis(tr("Filter Isolate Nodes\n\n" "Enables or disables displaying of isolate nodes. " "Isolate nodes are those with no edges...")); connect(editFilterNodesIsolatesAct, SIGNAL(toggled(bool)), this, SLOT(slotEditFilterNodesIsolates(bool))); editFilterEdgesByWeightAct = new QAction(QIcon(":/images/filter.png"), tr("Filter Edges by Weight"), this); editFilterEdgesByWeightAct -> setEnabled(true); editFilterEdgesByWeightAct -> setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E, Qt::CTRL + Qt::Key_F)); editFilterEdgesByWeightAct -> setStatusTip(tr("Temporarily filter edges of some weight out of the network")); editFilterEdgesByWeightAct -> setWhatsThis(tr("Filter Edges\n\n" "Filters Edge of some specific weight out of the network.")); connect(editFilterEdgesByWeightAct , SIGNAL(triggered()), this, SLOT(slotEditFilterEdgesByWeightDialog())); editFilterEdgesUnilateralAct = new QAction(tr("Disable unilateral edges"), this); editFilterEdgesUnilateralAct -> setEnabled(true); editFilterEdgesUnilateralAct -> setCheckable(true); editFilterEdgesUnilateralAct -> setChecked(false); editFilterEdgesUnilateralAct -> setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E, Qt::CTRL + Qt::Key_R)); editFilterEdgesUnilateralAct -> setStatusTip(tr("Temporarily disable all unilateral (non-reciprocal) edges in this relation. Keeps only \"strong\" ties.")); editFilterEdgesUnilateralAct -> setWhatsThis(tr("Unilateral edges\n\n" "In directed networks, a tie between two actors " "is unilateral when only one actor identifies the other " "as connected (i.e. friend, vote, etc). " "A unilateral tie is depicted as a single arc. " "These ties are considered weak, as opposed to " "reciprocal ties where both actors identify each other as connected. " "Strong ties are depicted as either a single undirected edge " "or as two reciprocated arcs between two nodes. " "By selecting this option, all unilateral edges in this relation will be disabled.")); connect(editFilterEdgesUnilateralAct , SIGNAL(triggered(bool)), this, SLOT(slotEditFilterEdgesUnilateral(bool))); /** Layout menu actions */ strongColorationAct = new QAction ( tr("Strong Structural"), this); strongColorationAct -> setStatusTip( tr("Nodes are assigned the same color if they have identical in and out neighborhoods") ); strongColorationAct -> setWhatsThis( tr("Click this to colorize nodes; Nodes are assigned the same color if they have identical in and out neighborhoods")); connect(strongColorationAct, SIGNAL(triggered() ), this, SLOT(slotLayoutColorationStrongStructural()) ); regularColorationAct = new QAction ( tr("Regular"), this); regularColorationAct -> setStatusTip( tr("Nodes are assigned the same color if they have " "neighborhoods of the same set of colors") ); regularColorationAct -> setWhatsThis( tr("Click this to colorize nodes; " "Nodes are assigned the same color if they have neighborhoods " "of the same set of colors")); connect(regularColorationAct, SIGNAL(triggered() ), this, SLOT(slotLayoutColorationRegular()) );//TODO randLayoutAct = new QAction( tr("Random"),this); randLayoutAct -> setShortcut(Qt::CTRL+Qt::SHIFT+Qt::Key_0); randLayoutAct -> setStatusTip(tr("Repositions all nodes in random places")); randLayoutAct -> setWhatsThis(tr("Random Layout\n\n Repositions all nodes in random places")); connect(randLayoutAct, SIGNAL(triggered()), this, SLOT(slotLayoutRandom())); randCircleLayoutAct = new QAction(tr("Random Circles"), this); randCircleLayoutAct -> setShortcut(Qt::CTRL+Qt::ALT+Qt::Key_0); randCircleLayoutAct ->setStatusTip(tr("Repositions the nodes randomly on circles")); randCircleLayoutAct-> setWhatsThis( tr("Random Circles Layout\n\n Repositions the nodes randomly on circles")); connect(randCircleLayoutAct, SIGNAL(triggered()), this, SLOT(slotLayoutCircularRandom())); layoutCircular_DC_Act = new QAction( tr("Degree Centrality"), this); layoutCircular_DC_Act -> setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_1); layoutCircular_DC_Act ->setStatusTip( tr("Layout all nodes on concentric circles of radius inversely " "proportional to their Degree Centrality.")); layoutCircular_DC_Act-> setWhatsThis( tr( "Degree Centrality Circular Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their Degree Centrality" "Nodes with higher DC score are closer to the centre." ) ); connect(layoutCircular_DC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex()) ); layoutCircular_CC_Act = new QAction( tr("Closeness Centrality"), this); layoutCircular_CC_Act -> setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_2); layoutCircular_CC_Act -> setStatusTip( tr("Layout all nodes on concentric circles of radius inversely " "proportional to their CC index.")); layoutCircular_CC_Act-> setWhatsThis( tr( "Closeness Centrality Circular Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their CC index." "Nodes having higher CC score are closer to the centre." )); connect(layoutCircular_CC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex())); layoutCircular_IRCC_Act = new QAction( tr("Influence Range Closeness Centrality"), this); layoutCircular_IRCC_Act -> setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_3); layoutCircular_IRCC_Act ->setStatusTip( tr( "Layout all nodes on concentric circles of radius inversely " "proportional to their IRCC index.")); layoutCircular_IRCC_Act-> setWhatsThis( tr( "Influence Range Closeness Centrality Circular Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their IRCC index." "Nodes having higher IRCC score are closer to the centre." )); connect(layoutCircular_IRCC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex())); layoutCircular_BC_Act = new QAction( tr("Betweenness Centrality"), this); layoutCircular_BC_Act -> setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_4); layoutCircular_BC_Act ->setStatusTip( tr( "Layout all nodes on concentric circles of radius inversely " "proportional to their BC index.")); layoutCircular_BC_Act-> setWhatsThis( tr( "Betweenness Centrality Circular Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their BC index." "Nodes having higher BC score are closer to the centre." )); connect(layoutCircular_BC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex())); layoutCircular_SC_Act = new QAction( tr("Stress Centrality"), this); layoutCircular_SC_Act -> setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_5); layoutCircular_SC_Act ->setStatusTip( tr( "Layout all nodes on concentric circles of radius inversely " "proportional to their SC index.")); layoutCircular_SC_Act-> setWhatsThis( tr( "Stress Centrality Circular Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their SC index." "Nodes having higher SC score are closer to the centre." )); connect(layoutCircular_SC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex())); layoutCircular_EC_Act = new QAction( tr("Eccentricity Centrality"), this); layoutCircular_EC_Act -> setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_6); layoutCircular_EC_Act ->setStatusTip( tr( "Layout all nodes on concentric circles of radius inversely " "proportional to their EC index.")); layoutCircular_EC_Act-> setWhatsThis( tr( "Eccentricity Centrality Circular Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their EC index." "Nodes having higher EC score are closer to the centre." )); connect(layoutCircular_EC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex())); layoutCircular_PC_Act = new QAction( tr("Power Centrality"), this); layoutCircular_PC_Act -> setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_7); layoutCircular_PC_Act ->setStatusTip( tr( "Layout all nodes on concentric circles of radius inversely " "proportional to their PC index.")); layoutCircular_PC_Act-> setWhatsThis( tr( "Power Centrality Circular Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their PC index." "Nodes having higher PC score are closer to the centre." )); connect(layoutCircular_PC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex())); layoutCircular_IC_Act = new QAction( tr("Information Centrality"), this); layoutCircular_IC_Act -> setEnabled(true); layoutCircular_IC_Act -> setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_8); layoutCircular_IC_Act -> setStatusTip( tr( "Layout all nodes on concentric circles of radius inversely " "proportional to their IC index.")); layoutCircular_IC_Act-> setWhatsThis( tr( "Information Centrality Circular Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their IC index." "Nodes having higher IC score are closer to the centre." )); connect(layoutCircular_IC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex())); layoutCircular_DP_Act = new QAction( tr("Degree Prestige"), this); layoutCircular_DP_Act->setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_I); layoutCircular_DP_Act ->setStatusTip( tr( "Layout all nodes on concentric circles of radius inversely " "proportional to their DP index.")); layoutCircular_DP_Act-> setWhatsThis( tr( "Degree Prestige Circular Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their DP index." "Nodes having higher DP score are closer to the centre." )); connect(layoutCircular_DP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex())); layoutCircular_PRP_Act = new QAction( tr("PageRank Prestige"), this); layoutCircular_PRP_Act ->setEnabled(true); layoutCircular_PRP_Act->setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_K); layoutCircular_PRP_Act ->setStatusTip( tr( "Layout all nodes on concentric circles of radius inversely " "proportional to their PRP index.")); layoutCircular_PRP_Act-> setWhatsThis( tr( "PageRank Prestige Circular Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their PRP index." "Nodes having higher PRP score are closer to the centre." )); connect(layoutCircular_PRP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex())); layoutCircular_PP_Act = new QAction( tr("Proximity Prestige"), this); layoutCircular_PP_Act ->setShortcut(Qt::CTRL + Qt::ALT+ Qt::Key_Y); layoutCircular_PP_Act ->setStatusTip( tr( "Layout all nodes on concentric circles of radius inversely " "proportional to their PP index.")); layoutCircular_PP_Act-> setWhatsThis( tr( "Proximity Prestige Circular Layout\n\n" "Repositions all nodes on concentric circles of radius " "inversely proportional to their PP index." "Nodes having higher PP score are closer to the centre." )); connect(layoutCircular_PP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutCircularByProminenceIndex())); layoutGuidesAct = new QAction(QIcon(":/images/gridlines.png"), tr("Layout GuideLines"), this); layoutGuidesAct ->setStatusTip(tr("Toggles layout guidelines on or off.")); layoutGuidesAct->setWhatsThis(tr("Layout Guidelines\n\n" "Layout Guidelines are circular or horizontal lines \n" "usually created when embedding prominence-based \n" "visualization models on the network.\n" "Disable this checkbox to hide guidelines")); layoutGuidesAct->setCheckable(true); layoutGuidesAct->setChecked(true); layoutLevel_DC_Act = new QAction( tr("Degree Centrality"), this); layoutLevel_DC_Act -> setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_1); layoutLevel_DC_Act ->setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their DC index.")); layoutLevel_DC_Act-> setWhatsThis( tr( "Degree Centrality Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their DC index." "Nodes having higher DC score are closer to the top.\n\n" ) ); connect(layoutLevel_DC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex()) ); layoutLevel_CC_Act = new QAction( tr("Closeness Centrality"), this); layoutLevel_CC_Act -> setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_2); layoutLevel_CC_Act -> setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their CC index.")); layoutLevel_CC_Act-> setWhatsThis( tr( "Closeness Centrality Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their CC index." "Nodes having higher CC score are closer to the top.\n\n" "This layout can be computed only for connected graphs. " )); connect(layoutLevel_CC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevel_IRCC_Act = new QAction( tr("Influence Range Closeness Centrality"), this); layoutLevel_IRCC_Act -> setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_3); layoutLevel_IRCC_Act ->setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their IRCC index.")); layoutLevel_IRCC_Act-> setWhatsThis( tr( "Influence Range Closeness Centrality Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their IRCC index." "Nodes having higher IRCC score are closer to the top.\n\n" "This layout can be computed for not connected graphs. " )); connect(layoutLevel_IRCC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevel_BC_Act = new QAction( tr("Betweenness Centrality"), this); layoutLevel_BC_Act -> setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_4); layoutLevel_BC_Act ->setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their BC index.")); layoutLevel_BC_Act-> setWhatsThis( tr( "Betweenness Centrality Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their BC index." "Nodes having higher BC score are closer to the top." )); connect(layoutLevel_BC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevel_SC_Act = new QAction( tr("Stress Centrality"), this); layoutLevel_SC_Act -> setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_5); layoutLevel_SC_Act ->setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their SC index.")); layoutLevel_SC_Act-> setWhatsThis( tr( "Stress Centrality Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their SC index." "Nodes having higher SC score are closer to the top." )); connect(layoutLevel_SC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevel_EC_Act = new QAction( tr("Eccentricity Centrality"), this); layoutLevel_EC_Act -> setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_6); layoutLevel_EC_Act ->setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their EC index.")); layoutLevel_EC_Act-> setWhatsThis( tr( "Eccentricity Centrality Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their EC index." "Nodes having higher EC score are closer to the top." )); connect(layoutLevel_EC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevel_PC_Act = new QAction( tr("Power Centrality"), this); layoutLevel_PC_Act -> setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_7); layoutLevel_PC_Act ->setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their PC index.")); layoutLevel_PC_Act-> setWhatsThis( tr( "Power Centrality Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their PC index." "Nodes having higher PC score are closer to the top." )); connect(layoutLevel_PC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevel_IC_Act = new QAction( tr("Information Centrality"), this); layoutLevel_IC_Act ->setEnabled(true); layoutLevel_IC_Act -> setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_8); layoutLevel_IC_Act ->setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their IC index.")); layoutLevel_IC_Act-> setWhatsThis( tr( "Information Centrality Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their IC index." "Nodes having higher IC score are closer to the top." )); connect(layoutLevel_IC_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevel_DP_Act = new QAction( tr("Degree Prestige"), this); layoutLevel_DP_Act -> setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_I); layoutLevel_DP_Act ->setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their DP index.")); layoutLevel_DP_Act-> setWhatsThis( tr( "Degree Prestige Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their DP index." "Nodes having higher DP score are closer to the top." )); connect(layoutLevel_DP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevel_PRP_Act = new QAction( tr("PageRank Prestige"), this); layoutLevel_PRP_Act -> setEnabled(true); layoutLevel_PRP_Act -> setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_K); layoutLevel_PRP_Act -> setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their PRP index.")); layoutLevel_PRP_Act-> setWhatsThis( tr( "PageRank Prestige Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their PRP index." "Nodes having higher PRP score are closer to the top." )); connect(layoutLevel_PRP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); layoutLevel_PP_Act = new QAction( tr("Proximity Prestige"), this); layoutLevel_PP_Act -> setEnabled(true); layoutLevel_PP_Act -> setShortcut(Qt::CTRL + Qt::SHIFT+ Qt::Key_Y); layoutLevel_PP_Act -> setStatusTip( tr( "Layout nodes on horizontal levels of height " "proportional to their PP index.")); layoutLevel_PP_Act-> setWhatsThis( tr( "Proximity Prestige Levels Layout\n\n" "Repositions all nodes on horizontal levels of height" "proportional to their PP index." "Nodes having higher PP score are closer to the top." )); connect(layoutLevel_PP_Act, SIGNAL(triggered()), this, SLOT(slotLayoutLevelByProminenceIndex())); springLayoutAct= new QAction(tr("Spring Embedder (Eades)"), this); springLayoutAct-> setShortcut(Qt::ALT + Qt::Key_1); springLayoutAct->setStatusTip(tr("All nodes repel each other while the connected ones are attracted as if connected by springs.")); springLayoutAct->setWhatsThis(tr("Spring Embedder Layout\n\n In this model, nodes are regarded as physical bodies (i.e. electrons) which exert repelling forces to each other, while edges are springs connecting adjacents nodes. Non-adjacent nodes repel each other while connected nodes are The algorithm continues until the system retains an equilibrium state in which all forces cancel each other. ")); connect(springLayoutAct, SIGNAL(triggered(bool)), this, SLOT(slotLayoutSpringEmbedder())); FRLayoutAct= new QAction( tr("Fruchterman-Reingold"), this); FRLayoutAct-> setShortcut(Qt::ALT + Qt::Key_2); FRLayoutAct->setStatusTip(tr("Repelling forces between all nodes, and attracting forces between adjacent nodes.")); FRLayoutAct->setWhatsThis(tr("Fruchterman-Reingold Layout\n\n Embeds a layout all nodes according to a model in which repelling forces are used between every pair of nodes, while attracting forces are used only between adjacent nodes. The algorithm continues until the system retains its equilibrium state where all forces cancel each other.")); connect(FRLayoutAct, SIGNAL(triggered()), this, SLOT(slotLayoutFruchterman())); nodeSizesByOutDegreeAct= new QAction(QIcon(":/images/nodeout.png"), tr("Node sizes by OutDegree"), this); nodeSizesByOutDegreeAct-> setShortcut(Qt::ALT + Qt::Key_3); nodeSizesByOutDegreeAct-> setStatusTip(tr("Resizes all nodes according to their outDegree.")); nodeSizesByOutDegreeAct ->setWhatsThis(tr("Node sizes by OutDegree) \n\n" "Adjusts the size of each node according to its " "OutDegree. The more out-linked a node is, " "the bigger will appear...")); nodeSizesByOutDegreeAct->setCheckable(true); nodeSizesByOutDegreeAct->setChecked(false); connect(nodeSizesByOutDegreeAct, SIGNAL(triggered(bool)), this, SLOT(slotLayoutNodeSizesByOutDegree(bool))); nodeSizesByInDegreeAct= new QAction( QIcon(":/images/nodein.png"),tr("Node sizes by InDegree"), this); nodeSizesByInDegreeAct-> setShortcut(Qt::ALT + Qt::Key_4); nodeSizesByInDegreeAct->setStatusTip( tr("Resizes all nodes according to their InDegree.")); nodeSizesByInDegreeAct-> setWhatsThis(tr("Node sizes by InDegree) \n\n" "This method adjusts the size of each node according " "to its InDegree. The more in-linked a node is, " "the bigger will appear...")); nodeSizesByInDegreeAct->setCheckable(true); nodeSizesByInDegreeAct->setChecked(false); connect(nodeSizesByInDegreeAct, SIGNAL(triggered(bool)), this, SLOT(slotLayoutNodeSizesByInDegree(bool))); /** Analysis menu actions */ analyzeMatrixAdjInvertAct = new QAction( QIcon(":/images/invertmatrix.png"), tr("Invert Adjacency Matrix"), this); analyzeMatrixAdjInvertAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_M, Qt::CTRL + Qt::Key_I) ); analyzeMatrixAdjInvertAct->setStatusTip(tr("Invert the adjacency matrix, if possible")); analyzeMatrixAdjInvertAct->setWhatsThis(tr("Invert Adjacency Matrix \n\n" "Inverts the adjacency matrix using linear algebra methods.")); connect(analyzeMatrixAdjInvertAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeMatrixAdjacencyInverse())); analyzeMatrixAdjTransposeAct = new QAction( QIcon(":/images/transposematrix.png"), tr("Transpose Adjacency Matrix"), this); analyzeMatrixAdjTransposeAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_M, Qt::CTRL + Qt::Key_T) ); analyzeMatrixAdjTransposeAct->setStatusTip(tr("View the transpose of adjacency matrix")); analyzeMatrixAdjTransposeAct->setWhatsThis(tr("Transpose Adjacency Matrix \n\n" "Computes and displays the adjacency matrix tranpose.")); connect(analyzeMatrixAdjTransposeAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeMatrixAdjacencyTranspose())); analyzeMatrixAdjCocitationAct = new QAction( QIcon(":/images/cocitation.png"), tr("Cocitation Matrix"), this); analyzeMatrixAdjCocitationAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_M, Qt::CTRL + Qt::Key_C) ); analyzeMatrixAdjCocitationAct->setStatusTip(tr("Compute the Cocitation matrix of this network.")); analyzeMatrixAdjCocitationAct->setWhatsThis(tr("Cocitation Matrix \n\n " "Computes and displays the cocitation matrix of the network. " "The Cocitation matrix, C=A*A^T, is a NxN matrix where " "each element (i,j) is the number of actors that have " "outbound ties/links to both actors i and j. ")); connect(analyzeMatrixAdjCocitationAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeMatrixAdjacencyCocitation())); analyzeMatrixDegreeAct = new QAction( QIcon(":/images/degreematrix.png"), tr("Degree Matrix"), this); analyzeMatrixDegreeAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_M, Qt::CTRL + Qt::Key_D) ); analyzeMatrixDegreeAct->setStatusTip(tr("Compute the Degree matrix of the network")); analyzeMatrixDegreeAct->setWhatsThis(tr("Degree Matrix " "\n\n Compute the Degree matrix of the network.")); connect(analyzeMatrixDegreeAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeMatrixDegree())); analyzeMatrixLaplacianAct = new QAction( QIcon(":/images/laplacian.png"), tr("Laplacian Matrix"), this); analyzeMatrixLaplacianAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_M, Qt::CTRL + Qt::Key_L) ); analyzeMatrixLaplacianAct->setStatusTip(tr("Compute the Laplacian matrix of the network")); analyzeMatrixLaplacianAct->setWhatsThis(tr("Laplacian Matrix \n\n" "Compute the Laplacian matrix of the network.")); connect(analyzeMatrixLaplacianAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeMatrixLaplacian())); analyzeGraphSymmetryAct = new QAction( QIcon(":/images/symmetry-edge.png"), tr("Symmetry Test"), this); analyzeGraphSymmetryAct -> setShortcut(Qt::SHIFT + Qt::Key_S); analyzeGraphSymmetryAct->setStatusTip(tr("Check whether the network is symmetric or not")); analyzeGraphSymmetryAct->setWhatsThis( tr("Symmetry\n\n" "Checks whether the network is symmetric or not. \n" "A network is symmetric when all edges are reciprocal, or, " "in mathematical language, when the adjacency matrix is " "symmetric.") ); connect(analyzeGraphSymmetryAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeSymmetryCheck())); analyzeGraphDistanceAct = new QAction( QIcon(":/images/distance.png"), tr("Geodesic Distance between 2 nodes"), this ); analyzeGraphDistanceAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_G) ); analyzeGraphDistanceAct->setStatusTip( tr("Compute the length of the shortest path (geodesic distance) between 2 nodes.")); analyzeGraphDistanceAct->setWhatsThis( tr("Distance\n\n" "Computes the geodesic distance between two nodes." "In graph theory, the geodesic distance of two " "nodes is the length (number of edges) of the shortest path " "between them.")); connect(analyzeGraphDistanceAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeDistance())); analyzeMatrixDistancesGeodesicAct = new QAction(QIcon(":/images/dm.png"), tr("Geodesic Distances Matrix"),this); analyzeMatrixDistancesGeodesicAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_M) ); analyzeMatrixDistancesGeodesicAct-> setStatusTip( tr("Compute the matrix of geodesic distances between all pair of nodes.") ); analyzeMatrixDistancesGeodesicAct-> setWhatsThis( tr("Distances Matrix\n\n" "Computes the matrix of distances between all " "pairs of actors/nodes in the social network." "A distances matrix is a n x n matrix, in which the " "(i,j) element is the distance from node i to node j" "The distance of two nodes is the length of the shortest path between them.") ); connect(analyzeMatrixDistancesGeodesicAct, SIGNAL(triggered()), this, SLOT( slotAnalyzeMatrixDistances() ) ); analyzeMatrixGeodesicsAct = new QAction(QIcon(":/images/dm.png"), tr("Geodesics Matrix"),this); analyzeMatrixGeodesicsAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_S)); analyzeMatrixGeodesicsAct->setStatusTip(tr("Compute the number of geodesic paths between each pair of nodes ")); analyzeMatrixGeodesicsAct->setWhatsThis( tr( "Geodesics Matrix\n\n" "Displays a n x n matrix, where the (i,j) element " "is the number of geodesics between node i and node j. " "A geodesic of two nodes is the shortest path between them.") ); connect(analyzeMatrixGeodesicsAct, SIGNAL(triggered()), this, SLOT( slotAnalyzeMatrixGeodesics()) ); analyzeGraphDiameterAct = new QAction(QIcon(":/images/diameter.png"), tr("Graph Diameter"),this); analyzeGraphDiameterAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_D)); analyzeGraphDiameterAct->setStatusTip(tr("Compute the diameter of the network, " "the maximum geodesic distance between any actors.")); analyzeGraphDiameterAct->setWhatsThis(tr("Diameter\n\n " "The Diameter of a social network is the maximum geodesic distance " "(maximum shortest path length) between any two nodes of the network.")); connect(analyzeGraphDiameterAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeDiameter())); averGraphDistanceAct = new QAction(QIcon(":/images/avdistance.png"), tr("Average Distance"),this); averGraphDistanceAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_A)); averGraphDistanceAct->setStatusTip(tr("Compute the average length of shortest paths for all possible pairs of nodes.")); averGraphDistanceAct->setWhatsThis( tr("Average Distance\n\n " "Computes the average length of shortest paths (geodesics) " "between all pairs of network actors (vertices in the graph). " "It is a measure of the efficiency or compactness of the network.")); connect(averGraphDistanceAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeDistanceAverage())); analyzeGraphEccentricityAct = new QAction(QIcon(":/images/eccentricity.png"), tr("Eccentricity"),this); analyzeGraphEccentricityAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_E ) ); analyzeGraphEccentricityAct->setStatusTip(tr("Compute the Eccentricity of each actor and group Eccentricity")); analyzeGraphEccentricityAct->setWhatsThis(tr("Eccentricity\n\n" "The eccentricity of each node i in a network " "or graph is the largest geodesic distance " "between node i and any other node j. " "Therefore, it reflects how far, at most, " "is each node from every other node. \n" "The maximum eccentricity is the graph diameter " "while the minimum is the graph radius.\n" "This index can be calculated in both graphs " "and digraphs but is usually best suited " "for undirected graphs. \n" "It can also be calculated in weighted graphs " "although the weight of each edge (v,u) in E is " "always considered to be 1.")); connect(analyzeGraphEccentricityAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeEccentricity())); analyzeGraphConnectednessAct = new QAction(QIcon(":/images/distance.png"), tr("Connectedness"), this); analyzeGraphConnectednessAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_C) ); analyzeGraphConnectednessAct->setStatusTip(tr("Check whether the network is a connected " "graph, a weakly connected digraph or " "a disconnected graph/digraph...")); analyzeGraphConnectednessAct->setWhatsThis(tr("Connectedness\n\n In graph theory, a " "graph is connected if there is a " "path between every pair of nodes. \n" "A digraph is strongly connected " "if there the a path from i to j and " "from j to i for all pairs (i,j).\n" "A digraph is weakly connected if at least " "a pair of nodes are joined by a semipath.\n" "A digraph or a graph is disconnected if " "at least one node is isolate." )); connect(analyzeGraphConnectednessAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeConnectedness())); analyzeGraphWalksAct = new QAction(QIcon(":/images/walk.png"), tr("Walks of a given length"),this); analyzeGraphWalksAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_W) ); analyzeGraphWalksAct->setStatusTip(tr("Compute the number of walks of a given length between any nodes.")); analyzeGraphWalksAct->setWhatsThis(tr("Walks of a given length\n\n" "A walk is a sequence of alternating vertices and edges " "such as v0e1, v1e2, " "v2e3, …, ekvk, " "where each edge, ei is defined as " "ei = {vi-1, vi}. " "This function counts the number of walks of a given " "length between each pair of nodes, by studying the powers of the sociomatrix.\n")); connect(analyzeGraphWalksAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeWalksLength() ) ); analyzeGraphWalksTotalAct = new QAction(QIcon(":/images/walk.png"), tr("Total Walks"),this); analyzeGraphWalksTotalAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_T) ); analyzeGraphWalksTotalAct->setStatusTip(tr("Calculate the total number of walks of every possible length between all nodes")); analyzeGraphWalksTotalAct->setWhatsThis(tr("Total Walks\n\n" "A walk is a sequence of alternating vertices " "and edges such as v0e1, " "v1e2, v2e3, …, " "ekvk, where each edge, ei " "is defined as ei = {vi-1, vi}. " "This function counts the number of walks of any length " "between each pair of nodes, by studying the powers of the sociomatrix. \n")); connect(analyzeGraphWalksTotalAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeWalksTotal() ) ); analyzeMatrixReachabilityAct = new QAction(QIcon(":/images/walk.png"), tr("Reachability Matrix"),this); analyzeMatrixReachabilityAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_M, Qt::CTRL + Qt::Key_R)); analyzeMatrixReachabilityAct->setStatusTip(tr("Compute the Reachability Matrix of the network.")); analyzeMatrixReachabilityAct->setWhatsThis(tr("Reachability Matrix\n\n" "Calculates the reachability matrix XR of " "the graph where the {i,j} element is 1 if " "the vertices i and j are reachable. \n\n" "Actually, this just checks whether the corresponding element " "of Distances matrix is not zero.\n")); connect(analyzeMatrixReachabilityAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeReachabilityMatrix() ) ); clusteringCoefAct = new QAction(QIcon(":/images/clucof.png"), tr("Local and Network Clustering Coefficient"),this); clusteringCoefAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G, Qt::CTRL + Qt::Key_L) ); clusteringCoefAct->setStatusTip(tr("Compute the Watts & Strogatz Clustering Coefficient for every actor and the network average.")); clusteringCoefAct->setWhatsThis(tr("Local and Network Clustering Coefficient\n\n" "The local Clustering Coefficient (Watts & Strogatz, 1998) " "of an actor quantifies how close " "the actor and her neighbors are to being a clique and " "can be used as an indication of network transitivity. \n")); connect(clusteringCoefAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeClusteringCoefficient() ) ); analyzeCommunitiesCliquesAct = new QAction(QIcon(":/images/clique.png"), tr("Clique Census"),this); analyzeCommunitiesCliquesAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_U, Qt::CTRL + Qt::Key_C)); analyzeCommunitiesCliquesAct->setStatusTip(tr("Compute the clique census: find all maximal connected subgraphs.")); analyzeCommunitiesCliquesAct->setWhatsThis(tr("Clique Census\n\n" "Produces the census of network cliques (maximal connected subgraphs), " "along with disaggregation by actor and co-membership information. ")); connect(analyzeCommunitiesCliquesAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeCommunitiesCliqueCensus() ) ); analyzeCommunitiesTriadCensusAct = new QAction(QIcon(":/images/triad.png"), tr("Triad Census (M-A-N labeling)"),this); analyzeCommunitiesTriadCensusAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_U, Qt::CTRL + Qt::Key_T) ); analyzeCommunitiesTriadCensusAct->setStatusTip(tr("Calculate the triad census for all actors.")); analyzeCommunitiesTriadCensusAct->setWhatsThis(tr("Triad Census\n\n" "A triad census counts all the different kinds of observed triads " "within a network and codes them according to their number of mutual, " "asymmetric and non-existent dyads using the M-A-N labeling scheme. \n")); connect(analyzeCommunitiesTriadCensusAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeCommunitiesTriadCensus() ) ); analyzeStrEquivalencePearsonAct = new QAction(QIcon(":/images/similarity.png"), tr("Pearson correlation coefficients"),this); analyzeStrEquivalencePearsonAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_T, Qt::CTRL + Qt::Key_P) ); analyzeStrEquivalencePearsonAct->setStatusTip( tr("Compute Pearson Correlation Coefficients between pairs of actors. " "Most useful with valued/weighted ties (non-binary). ")); analyzeStrEquivalencePearsonAct->setWhatsThis( tr("Pearson correlation coefficients\n\n" "Computes a correlation matrix, where the elements are the " "Pearson correlation coefficients between pairs of actors " "in terms of their tie profiles or distances (in, out or both). \n\n" "The Pearson product-moment correlation coefficient (PPMCC or PCC or Pearson's r)" "is a measure of the linear dependence/association between two variables X and Y. \n\n" "This correlation measure of similarity is particularly useful " "when ties are valued/weighted denoting strength, cost or probability.\n\n" "Note that in very sparse networks (very low density), measures such as" "\"exact matches\", \"correlation\" and \"distance\" " "will show little variation among the actors, causing " "difficulty in classifying the actors in structural equivalence classes.")); connect(analyzeStrEquivalencePearsonAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeStrEquivalencePearsonDialog() ) ); analyzeStrEquivalenceMatchesAct = new QAction(QIcon(":/images/similarity.png"), tr("Similarity by measure (Exact, Jaccard, Hamming, Cosine, Euclidean)"),this); analyzeStrEquivalenceMatchesAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_T, Qt::CTRL + Qt::Key_E) ); analyzeStrEquivalenceMatchesAct->setStatusTip(tr("Compute a pair-wise actor similarity " "matrix based on a measure of their ties (or distances) \"matches\" .")); analyzeStrEquivalenceMatchesAct->setWhatsThis( tr("Actor Similarity by measure\n\n" "Computes a pair-wise actor similarity matrix, where each element (i,j) is " "the ratio of tie (or distance) matches of actors i and j to all other actors. \n\n" "SocNetV supports the following matching measures: " "Simple Matching (Exact Matches)" "Jaccard Index (Positive Matches or Co-citation)" "Hamming distance" "Cosine similarity" "Euclidean distance" "For instance, if you select Exact Matches, a matrix element (i,j) = 0.5, " "means that actors i and j have the same ties present or absent " "to other actors 50% of the time. \n\n" "These measures of similarity are particularly useful " "when ties are binary (not valued).\n\n" "Note that in very sparse networks (very low density), measures such as" "\"exact matches\", \"correlation\" and \"distance\" " "will show little variation among the actors, causing " "difficulty in classifying the actors in structural equivalence classes.")); connect(analyzeStrEquivalenceMatchesAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeStrEquivalenceSimilarityMeasureDialog() ) ); analyzeStrEquivalenceTieProfileDissimilaritiesAct = new QAction(QIcon(":/images/dm.png"), tr("Tie Profile Dissimilarities/Distances"),this); analyzeStrEquivalenceTieProfileDissimilaritiesAct -> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_T, Qt::CTRL + Qt::Key_T) ); analyzeStrEquivalenceTieProfileDissimilaritiesAct-> setStatusTip( tr("Compute tie profile dissimilarities/distances " "(Euclidean, Manhattan, Jaccard, Hamming) between all pair of nodes.") ); analyzeStrEquivalenceTieProfileDissimilaritiesAct-> setWhatsThis( tr("Tie Profile Dissimilarities/Distances\n\n" "Computes a matrix of tie profile distances/dissimilarities " "between all pairs of actors/nodes in the social network " "using an ordinary metric such as Euclidean distance, " "Manhattan distance, Jaccard distance or Hamming distance)." "The resulted distance matrix is a n x n matrix, in which the " "(i,j) element is the distance or dissimilarity between " "the tie profiles of node i and node j." ) ); connect(analyzeStrEquivalenceTieProfileDissimilaritiesAct, SIGNAL(triggered()), this, SLOT( slotAnalyzeStrEquivalenceDissimilaritiesDialog() ) ); analyzeStrEquivalenceClusteringHierarchicalAct = new QAction(QIcon(":/images/hierarchical.png"), tr("Hierarchical clustering"),this); analyzeStrEquivalenceClusteringHierarchicalAct-> setShortcut( QKeySequence(Qt::CTRL + Qt::Key_T, Qt::CTRL + Qt::Key_H)); analyzeStrEquivalenceClusteringHierarchicalAct->setStatusTip( tr("Perform agglomerative cluster analysis of the actors in the social network")); analyzeStrEquivalenceClusteringHierarchicalAct->setWhatsThis( tr("Hierarchical clustering\n\n" "Hierarchical clustering (or hierarchical cluster analysis, HCA) " "is a method of cluster analysis which builds a hierarchy " "of clusters, based on their elements dissimilarity. " "In SNA context these clusters usually consist of " "network actors. \n" "This method takes the social network distance matrix as input and uses " "the Agglomerative \"bottom up\" approach where each " "actor starts in its own cluster (Level 0). In each subsequent Level, " "as we move up the clustering hierarchy, a pair of clusters " "are merged into a larger cluster, until " "all actors end up in the same cluster. " "To decide which clusters should be combined at each level, a measure of " "dissimilarity between sets of observations is required. " "This measure consists of a metric for the distance between actors " "(i.e. manhattan distance) and a linkage criterion (i.e. single-linkage clustering). " "This linkage criterion (essentially a definition of distance between clusters), " "differentiates between the different HCA methods." "Note that the complexity of agglomerative clustering is O( n^2 log(n) ), " "therefore is too slow for large data sets." )); connect(analyzeStrEquivalenceClusteringHierarchicalAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeStrEquivalenceClusteringHierarchicalDialog() ) ); cDegreeAct = new QAction(tr("Degree Centrality (DC)"),this); cDegreeAct-> setShortcut(Qt::CTRL + Qt::Key_1); cDegreeAct ->setStatusTip(tr("Compute Degree Centrality indices for every actor and group Degree Centralization.")); cDegreeAct ->setWhatsThis( tr( "Degree Centrality (DC)\n\n" "For each node v, the DC index is the number of edges " "attached to it (in undirected graphs) or the total number " "of arcs (outLinks) starting from it (in digraphs).\n" "This is often considered a measure of actor activity. \n\n" "This index can be calculated in both graphs and digraphs " "but is usually best suited for undirected graphs. " "It can also be calculated in weighted graphs. " "In weighted relations, DC is the sum of weights of all " "edges/outLinks attached to v.")); connect(cDegreeAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeCentralityDegree())); cClosenessAct = new QAction(tr("Closeness Centrality (CC)"), this); cClosenessAct-> setShortcut(Qt::CTRL + Qt::Key_2); cClosenessAct ->setStatusTip( tr( "Compute Closeness Centrality indices for every actor and group Closeness Centralization.")); cClosenessAct ->setWhatsThis( tr("Closeness Centrality (CC)\n\n" "For each node v, CC the inverse sum of " "the shortest distances between v and every other node. CC is " "interpreted as the ability to access information through the " "\"grapevine\" of network members. Nodes with high closeness " "centrality are those who can reach many other nodes in few steps. " "\n\nThis index can be calculated in both graphs and digraphs. " "It can also be calculated in weighted graphs although the weight of " "each edge (v,u) in E is always considered to be 1. ")); connect(cClosenessAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeCentralityCloseness())); cInfluenceRangeClosenessAct = new QAction(tr("Influence Range Closeness Centrality (IRCC)"), this); cInfluenceRangeClosenessAct-> setShortcut(Qt::CTRL + Qt::Key_3); cInfluenceRangeClosenessAct ->setStatusTip( tr("Compute Influence Range Closeness Centrality indices for every actor " "focusing on how proximate each one is" "to others in its influence range")); cInfluenceRangeClosenessAct ->setWhatsThis( tr("Influence Range Closeness Centrality (IRCC)\n\n" "For each node v, IRCC is the standardized inverse average distance " "between v and every reachable node.\n" "This improved CC index is optimized for graphs and directed graphs which " "are not strongly connected. Unlike the ordinary CC, which is the inverted " "sum of distances from node v to all others (thus undefined if a node is isolated " "or the digraph is not strongly connected), IRCC considers only " "distances from node v to nodes in its influence range J (nodes reachable from v). " "The IRCC formula used is the ratio of the fraction of nodes reachable by v " "(|J|/(n-1)) to the average distance of these nodes from v (sum(d(v,j))/|J|")); connect(cInfluenceRangeClosenessAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeCentralityClosenessIR())); cBetweennessAct = new QAction(tr("Betweenness Centrality (BC)"), this); cBetweennessAct-> setShortcut(Qt::CTRL + Qt::Key_4); cBetweennessAct->setWhatsThis(tr("Betweenness Centrality (BC)\n\n" "For each node v, BC is the ratio of all geodesics between pairs of nodes which run through v. " "It reflects how often an node lies on the geodesics between the other nodes of the network. " "It can be interpreted as a measure of control. " "A node which lies between many others is assumed to have a higher likelihood of being able " "to control information flow in the network. \n\n" "Note that betweenness centrality assumes that all geodesics " "have equal weight or are equally likely to be chosen for the flow of information " "between any two nodes. This is reasonable only on \"regular\" networks where all " "nodes have similar degrees. On networks with significant degree variance you might want " "to try informational centrality instead. \n\nThis index can be calculated in both graphs " "and digraphs but is usually best suited for undirected graphs. It can also be calculated" " in weighted graphs although the weight of each edge (v,u) in E is always considered to be 1.")); cBetweennessAct->setStatusTip(tr("Compute Betweenness Centrality indices and group Betweenness Centralization.")); connect(cBetweennessAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeCentralityBetweenness())); cStressAct = new QAction(tr("Stress Centrality (SC)"), this); cStressAct-> setShortcut(Qt::CTRL + Qt::Key_5); cStressAct->setStatusTip(tr("Compute Stress Centrality indices for every actor and group Stress Centralization.")); cStressAct->setWhatsThis(tr("Stress Centrality (SC)\n\n" "For each node v, SC is the total number of geodesics between all other nodes which run through v. " "A node with high SC is considered 'stressed', since it is traversed by a high number of geodesics. " "When one node falls on all other geodesics between all the remaining (N-1) nodes, " "then we have a star graph with maximum Stress Centrality. \n\n" "This index can be calculated in both graphs and digraphs but is usually best suited for undirected graphs. " "It can also be calculated in weighted graphs although the weight of each edge (v,u) in E is always considered to be 1.")); connect(cStressAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeCentralityStress())); cEccentAct = new QAction(tr("Eccentricity Centrality (EC)"), this); cEccentAct-> setShortcut(Qt::CTRL + Qt::Key_6); cEccentAct->setStatusTip(tr("Compute Eccentricity Centrality indices for each node.")); cEccentAct->setWhatsThis( tr("Eccentricity Centrality (EC)\n\n For each node i, " "the EC is the inverse of the maximum geodesic distance " "of that v to all other nodes in the network. \n" "Nodes with high EC have short distances to all other nodes " "This index can be calculated in both graphs and digraphs " "but is usually best suited for undirected graphs. " "It can also be calculated in weighted graphs although the weight of each edge (v,u) in E is always considered to be 1.")); connect(cEccentAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeCentralityEccentricity())); cPowerAct = new QAction(tr("Gil and Schmidt Power Centrality (PC)"), this); cPowerAct-> setShortcut(Qt::CTRL + Qt::Key_7); cPowerAct->setStatusTip(tr("Compute Power Centrality indices (aka Gil-Schmidt Power Centrality) for every actor and group Power Centralization")); cPowerAct->setWhatsThis(tr("Power Centrality (PC)\n\n " "For each node v, this index sums its degree (with weight 1), with the size of the 2nd-order neighbourhood (with weight 2), and in general, with the size of the kth order neighbourhood (with weight k). Thus, for each node in the network the most important other nodes are its immediate neighbours and then in decreasing importance the nodes of the 2nd-order neighbourhood, 3rd-order neighbourhood etc. For each node, the sum obtained is normalised by the total numbers of nodes in the same component minus 1. Power centrality has been devised by Gil-Schmidt. \n\nThis index can be calculated in both graphs and digraphs but is usually best suited for undirected graphs. It can also be calculated in weighted graphs although the weight of each edge (v,u) in E is always considered to be 1 (therefore not considered).")); connect(cPowerAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeCentralityPower())); cInformationAct = new QAction(tr("Information Centrality (IC)"), this); cInformationAct-> setShortcut(Qt::CTRL + Qt::Key_8); cInformationAct->setEnabled(true); cInformationAct->setStatusTip(tr("Compute Information Centrality indices and group Information Centralization")); cInformationAct->setWhatsThis( tr("Information Centrality (IC)\n\n" "Information centrality counts all paths between " "nodes weighted by strength of tie and distance. " "This centrality measure developed by Stephenson and Zelen (1989) " "focuses on how information might flow through many different paths. \n\n" "This index should be calculated only for graphs. \n\n" "Note: To compute this index, SocNetV drops all isolated nodes.")); connect(cInformationAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeCentralityInformation())); cEigenvectorAct = new QAction(tr("Eigenvector Centrality (EVC)"), this); cEigenvectorAct-> setShortcut(Qt::CTRL + Qt::Key_9); cEigenvectorAct->setEnabled(true); cEigenvectorAct->setStatusTip(tr("Compute Eigenvector Centrality indices and group Eigenvector Centralization")); cEigenvectorAct->setWhatsThis( tr("Eigenvector Centrality (EVC)\n\n" "Computes the Eigenvector centrality of each node in a social network " "which is defined as the ith element of the leading eigenvector " "of the adjacency matrix. The leading eigenvector is the " "eigenvector corresponding to the largest positive eigenvalue." "The Eigenvector Centrality, proposed by Bonacich (1989), is " "an extension of the simpler Degree Centrality because it gives " "each actor a score proportional to the scores of its neighbors. " "Thus, a node may be important, in terms of its EC, because it " "has lots of ties or it has fewer ties to important other nodes.")); connect(cEigenvectorAct, SIGNAL(triggered()), this, SLOT(slotAnalyzeCentralityEigenvector())); cInDegreeAct = new QAction(tr("Degree Prestige (DP)"), this); cInDegreeAct->setStatusTip(tr("Compute Degree Prestige (InDegree) indices ")); cInDegreeAct-> setShortcut(Qt::CTRL + Qt::Key_I); cInDegreeAct->setWhatsThis(tr("InDegree (Degree Prestige)\n\n" "For each node k, this the number of arcs ending at k. " "Nodes with higher in-degree are considered more prominent among others. " "In directed graphs, this index measures the prestige of each node/actor. " "Thus it is called Degree Prestige. " "Nodes who are prestigious tend to receive many nominations or choices (in-links). " "The largest the index is, the more prestigious is the node. \n\n" "This index can be calculated only for digraphs. " "In weighted relations, DP is the sum of weights of all arcs/inLinks ending at node v.")); connect(cInDegreeAct, SIGNAL(triggered()), this, SLOT(slotAnalyzePrestigeDegree())); cPageRankAct = new QAction(tr("PageRank Prestige (PRP)"), this); cPageRankAct-> setShortcut(Qt::CTRL + Qt::Key_K); cPageRankAct->setEnabled(true); cPageRankAct->setStatusTip(tr("Compute PageRank Prestige indices for every actor")); cPageRankAct->setWhatsThis(tr("PageRank Prestige\n\n" "An importance ranking for each node based on the link structure of the network. " "PageRank, developed by Page and Brin (1997), focuses on how nodes are " "connected to each other, treating each edge from a node as a citation/backlink/vote to another. " "In essence, for each node PageRank counts all backlinks to it, " "but it does so by not counting all edges equally while it " "normalizes each edge from a node by the total number of edges from it. " "PageRank is calculated iteratively and it corresponds to the principal " "eigenvector of the normalized link matrix. \n\n" "This index can be calculated in both graphs and digraphs but is " "usually best suited for directed graphs since it is a prestige measure. " "It can also be calculated in weighted graphs. " "In weighted relations, each backlink to a node v from another node u is " "considered to have weight=1 but it is normalized by the sum of " "outLinks weights (outDegree) of u. Therefore, nodes with high outLink " "weights give smaller percentage of their PR to node v.")); connect(cPageRankAct, SIGNAL(triggered()), this, SLOT(slotAnalyzePrestigePageRank())); cProximityPrestigeAct = new QAction(tr("Proximity Prestige (PP)"), this); cProximityPrestigeAct-> setShortcut(Qt::CTRL + Qt::Key_Y); cProximityPrestigeAct->setEnabled(true); cProximityPrestigeAct->setStatusTip(tr("Calculate and display Proximity Prestige (digraphs only)")); cProximityPrestigeAct ->setWhatsThis( tr("Proximity Prestige (PP) \n\n" "This index measures how proximate a node v is to the nodes " "in its influence domain I (the influence domain I of a node " "is the number of other nodes that can reach it).\n\n" "In PP calculation, proximity is based on distances to rather " "than distances from node v. \n" "To put it simply, in PP what matters is how close are all " "the other nodes to node v. \n\n" "The algorithm takes the average distance to node v of all " "nodes in its influence domain, standardizes it by " "multiplying with (N-1)/I and takes its reciprocal. " "In essence, the formula SocNetV uses to calculate PP " "is the ratio of the fraction of nodes that can reach node v, " "to the average distance of that nodes to v: \n" "PP = (I/(N-1))/(sum{d(u,v)}/I) \n" "where the sum is over all nodes in I.")); connect(cProximityPrestigeAct, SIGNAL(triggered()), this, SLOT(slotAnalyzePrestigeProximity())); /** Options menu actions */ optionsNodeNumbersVisibilityAct = new QAction( tr("Display Node Numbers"), this ); optionsNodeNumbersVisibilityAct->setStatusTip( tr("Toggle displaying of node numbers (this session only)")); optionsNodeNumbersVisibilityAct->setWhatsThis( tr("Display Node Numbers\n\n" "Enables or disables displaying of node numbers\n" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); optionsNodeNumbersVisibilityAct->setCheckable (true); optionsNodeNumbersVisibilityAct->setChecked ( ( appSettings["initNodeNumbersVisibility"] == "true" ) ? true: false ); connect(optionsNodeNumbersVisibilityAct, SIGNAL(triggered(bool)), this, SLOT(slotOptionsNodeNumbersVisibility(bool))); optionsNodeNumbersInsideAct = new QAction(tr("Display Numbers Inside Nodes"), this ); optionsNodeNumbersInsideAct->setStatusTip( tr("Toggle displaying of numbers inside nodes (this session only)")); optionsNodeNumbersInsideAct->setWhatsThis( tr("Display Numbers Inside Nodes\n\n" "Enables or disables displaying node numbers inside nodes.\n" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); optionsNodeNumbersInsideAct->setCheckable (true); optionsNodeNumbersInsideAct->setChecked( ( appSettings["initNodeNumbersInside"] == "true" ) ? true: false ); connect(optionsNodeNumbersInsideAct, SIGNAL(triggered(bool)), this, SLOT(slotOptionsNodeNumbersInside(bool))); optionsNodeLabelsVisibilityAct= new QAction(tr("Display Node Labels"), this ); optionsNodeLabelsVisibilityAct->setStatusTip( tr("Toggle displaying of node labels (this session only)")); optionsNodeLabelsVisibilityAct->setWhatsThis( tr("Display Node Labels\n\n" "Enables or disables node labels.\n" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); optionsNodeLabelsVisibilityAct->setCheckable (true); optionsNodeLabelsVisibilityAct->setChecked( ( appSettings["initNodeLabelsVisibility"] == "true" ) ? true: false ); connect(optionsNodeLabelsVisibilityAct, SIGNAL(toggled(bool)), this, SLOT(slotOptionsNodeLabelsVisibility(bool))); optionsEdgesVisibilityAct = new QAction(tr("Display Edges"), this); optionsEdgesVisibilityAct->setStatusTip(tr("Toggle displaying edges (this session only)")); optionsEdgesVisibilityAct->setWhatsThis( tr("Display Edges\n\n" "Enables or disables displaying of edges" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); optionsEdgesVisibilityAct->setCheckable(true); optionsEdgesVisibilityAct->setChecked( (appSettings["initEdgesVisibility"] == "true") ? true: false ); connect(optionsEdgesVisibilityAct, SIGNAL(triggered(bool)), this, SLOT(slotOptionsEdgesVisibility(bool)) ); optionsEdgeWeightNumbersAct = new QAction(tr("Display Edge Weights"), this); optionsEdgeWeightNumbersAct->setStatusTip( tr("Toggle displaying of numbers of edge weights (this session only)")); optionsEdgeWeightNumbersAct->setWhatsThis( tr("Display Edge Weights\n\n" "Enables or disables displaying edge weight numbers.\n" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); optionsEdgeWeightNumbersAct->setCheckable(true); optionsEdgeWeightNumbersAct->setChecked( (appSettings["initEdgeWeightNumbersVisibility"] == "true") ? true: false ); connect(optionsEdgeWeightNumbersAct, SIGNAL(triggered(bool)), this, SLOT(slotOptionsEdgeWeightNumbersVisibility(bool)) ); considerEdgeWeightsAct = new QAction(tr("Consider Edge Weights in Calculations"), this); considerEdgeWeightsAct-> setStatusTip( tr("Toggle considering edge weights during calculations " "(i.e. distances, centrality, etc) (this session only)")); considerEdgeWeightsAct-> setWhatsThis( tr("Consider Edge Weights in Calculations\n\n" "Enables or disables considering edge weights during " "calculations (i.e. distances, centrality, etc).\n" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); considerEdgeWeightsAct->setCheckable(true); considerEdgeWeightsAct->setChecked(false); connect(considerEdgeWeightsAct, SIGNAL(triggered(bool)), this, SLOT(slotOptionsEdgeWeightsDuringComputation(bool)) ); optionsEdgeLabelsAct = new QAction(tr("Display Edge Labels"), this); optionsEdgeLabelsAct->setStatusTip( tr("Toggle displaying of Edge labels, if any (this session only)")); optionsEdgeLabelsAct->setWhatsThis( tr("Display Edge Labes\n\n" "Enables or disables displaying edge labels.\n" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); optionsEdgeLabelsAct->setCheckable(true); optionsEdgeLabelsAct->setChecked( (appSettings["initEdgeLabelsVisibility"] == "true") ? true: false ); connect(optionsEdgeLabelsAct, SIGNAL(triggered(bool)), this, SLOT(slotOptionsEdgeLabelsVisibility(bool)) ); optionsEdgeArrowsAct = new QAction( tr("Display Edge Arrows"),this); optionsEdgeArrowsAct->setStatusTip( tr("Toggle displaying directional Arrows on edges (this session only)")); optionsEdgeArrowsAct->setWhatsThis( tr("Display edge Arrows\n\n" "Enables or disables displaying of arrows on edges.\n\n" "Useful if all links are reciprocal (undirected graph).\n" "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); optionsEdgeArrowsAct->setCheckable(true); optionsEdgeArrowsAct->setChecked( (appSettings["initEdgeArrows"]=="true") ? true: false ); connect(optionsEdgeArrowsAct, SIGNAL(triggered(bool)), this, SLOT(slotOptionsEdgeArrowsVisibility(bool)) ); optionsEdgeThicknessPerWeightAct = new QAction( tr("Edge Thickness reflects Weight"), this); optionsEdgeThicknessPerWeightAct->setStatusTip(tr("Draw edges as thick as their weights (if specified)")); optionsEdgeThicknessPerWeightAct->setWhatsThis( tr("Edge thickness reflects weight\n\n" "Click to toggle having all edges as thick as their weight (if specified)")); optionsEdgeThicknessPerWeightAct->setCheckable(true); optionsEdgeThicknessPerWeightAct->setChecked( (appSettings["initEdgeThicknessPerWeight"]=="true") ? true: false ); connect(optionsEdgeThicknessPerWeightAct, SIGNAL(triggered(bool)), this, SLOT(slotOptionsEdgeThicknessPerWeight(bool)) ); optionsEdgeThicknessPerWeightAct->setEnabled(false); drawEdgesBezier = new QAction( tr("Bezier Curves"), this); drawEdgesBezier->setStatusTip(tr("Draw Edges as Bezier curves")); drawEdgesBezier->setWhatsThis( tr("Edges Bezier\n\n" "Enable or disables drawing Edges as Bezier curves." "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); drawEdgesBezier->setCheckable(true); drawEdgesBezier->setChecked ( (appSettings["initEdgeShape"]=="bezier") ? true: false ); drawEdgesBezier->setEnabled(false); connect(drawEdgesBezier, SIGNAL(triggered(bool)), this, SLOT(slotOptionsEdgesBezier(bool)) ); changeBackColorAct = new QAction(QIcon(":/images/color.png"), tr("Change Background Color"), this); changeBackColorAct->setStatusTip(tr("Change the canvasbackground color")); changeBackColorAct->setWhatsThis(tr("Background Color\n\n" "Changes the background color of the canvas")); connect(changeBackColorAct, SIGNAL(triggered()), this, SLOT(slotOptionsBackgroundColor())); backgroundImageAct = new QAction(tr("Background Image (this session)"), this); backgroundImageAct->setStatusTip( tr("Select and display a custom image in the background" "(for this session only)")); backgroundImageAct->setWhatsThis( tr("Background image\n\n" "Enable to select an image file from your computer, " "which will be displayed in the background instead of plain color." "This setting will apply to this session only. \n" "To permanently change it, use Settings & Preferences")); backgroundImageAct->setCheckable(true); backgroundImageAct->setChecked(false); connect(backgroundImageAct, SIGNAL(triggered(bool)), this, SLOT(slotOptionsBackgroundImageSelect(bool))); openSettingsAct = new QAction(QIcon(":/images/appsettings.png"), tr("Settings"), this); openSettingsAct->setShortcut(Qt::CTRL + Qt::Key_Comma); openSettingsAct->setEnabled(true); openSettingsAct->setToolTip( tr("Open the Settings dialog where you can save your preferences " "for all future sessions")); openSettingsAct->setStatusTip( tr("Open the Settings dialog to save your preferences " "for all future sessions")); openSettingsAct->setWhatsThis( tr("Settings\n\n" "Opens the Settings dialog where you can edit and save settings " "permanently for all subsequent sessions.")); connect(openSettingsAct, SIGNAL(triggered()), this, SLOT(slotOpenSettingsDialog())); /** Help menu actions */ helpApp = new QAction(QIcon(":/images/help.png"), tr("Manual"), this); helpApp -> setShortcut(Qt::Key_F1); helpApp->setStatusTip(tr("Read the manual...")); helpApp->setWhatsThis(tr("Manual\n\nDisplays the documentation of SocNetV")); connect(helpApp, SIGNAL(triggered()), this, SLOT(slotHelp())); tipsApp = new QAction(QIcon(":/images/help-hint.png"), tr("Tip of the Day"), this); tipsApp->setStatusTip(tr("Read useful tips")); tipsApp->setWhatsThis(tr("Quick Tips\n\nDisplays some useful and quick tips")); connect(tipsApp, SIGNAL(triggered()), this, SLOT(slotHelpTips())); helpCheckUpdatesApp = new QAction( QIcon(":/images/download.png"), tr("Check for Updates"), this); helpCheckUpdatesApp->setStatusTip(tr("Open a browser to SocNetV website " "to check for a new version...")); helpCheckUpdatesApp->setWhatsThis(tr("Check Updates\n\n" "Open a browser to SocNetV website so " "that you can check yourself for updates")); connect(helpCheckUpdatesApp, SIGNAL(triggered()), this, SLOT(slotHelpCheckUpdates())); helpAboutApp = new QAction(tr("About SocNetV"), this); helpAboutApp->setStatusTip(tr("About SocNetV")); helpAboutApp->setWhatsThis(tr("About\n\nBasic information about SocNetV")); connect(helpAboutApp, SIGNAL(triggered()), this, SLOT(slotHelpAbout())); helpAboutQt = new QAction(QIcon(":/images/qt.png"), tr("About Qt"), this); helpAboutQt->setStatusTip(tr("About Qt")); helpAboutQt->setWhatsThis(tr("About\n\nAbout Qt")); connect(helpAboutQt, SIGNAL(triggered()), this, SLOT(slotAboutQt() ) ); } /** Creates and populates the MenuBar */ void MainWindow::initMenuBar() { /** menuBar entry networkMenu */ networkMenu = menuBar()->addMenu(tr("&Network")); networkMenu -> addAction(networkNew); networkMenu -> addAction(networkOpen); networkMenu -> addSeparator(); recentFilesSubMenu = new QMenu(tr("Recent files...")); for (int i = 0; i < MaxRecentFiles; ++i) recentFilesSubMenu->addAction(recentFileActs[i]); slotNetworkFileRecentUpdateActions(); networkMenu ->addMenu (recentFilesSubMenu ); networkMenu -> addSeparator(); importSubMenu = new QMenu(tr("Import ...")); importSubMenu -> setIcon(QIcon(":/images/import.png")); importSubMenu -> addAction(networkImportGML); importSubMenu -> addAction(networkImportPajek); importSubMenu -> addAction(networkImportSM); importSubMenu -> addAction(networkImportTwoModeSM); importSubMenu -> addAction(networkImportList); importSubMenu -> addAction(networkImportDL); importSubMenu -> addAction(networkImportDot); networkMenu ->addMenu (importSubMenu); networkMenu -> addSeparator(); networkMenu -> addAction (openTextEditorAct); networkMenu -> addAction (networkViewFileAct); networkMenu -> addSeparator(); networkMenu -> addAction (networkViewSociomatrixAct); networkMenu -> addAction (networkViewSociomatrixPlotAct); networkMenu -> addSeparator(); networkMenu -> addAction (networkDataSetSelectAct); networkMenu -> addSeparator(); randomNetworkMenu = new QMenu(tr("Create Random Network...")); randomNetworkMenu -> setIcon(QIcon(":/images/random.png")); networkMenu ->addMenu (randomNetworkMenu); randomNetworkMenu -> addAction (createScaleFreeRandomNetworkAct); randomNetworkMenu -> addAction (createSmallWorldRandomNetworkAct); randomNetworkMenu -> addAction (createErdosRenyiRandomNetworkAct ); randomNetworkMenu -> addAction (createRegularRandomNetworkAct); randomNetworkMenu -> addAction (createLatticeNetworkAct); // createGaussianRandomNetworkAct -> addTo(randomNetworkMenu); networkMenu->addSeparator(); networkMenu -> addAction(webCrawlerAct); networkMenu -> addSeparator(); networkMenu -> addAction(networkSave); networkMenu -> addAction(networkSaveAs); networkMenu -> addSeparator(); exportSubMenu = networkMenu -> addMenu(tr("Export...")); exportSubMenu -> addAction (networkExportBMP); exportSubMenu -> addAction (networkExportPNG); exportSubMenu -> addAction (networkExportPDF); exportSubMenu -> addSeparator(); exportSubMenu -> addAction (networkExportSM); exportSubMenu -> addAction (networkExportPajek); //exportSubMenu -> addAction (networkExportList); //exportSubMenu -> addAction (networkExportDL); //exportSubMenu -> addAction (networkExportGW); networkMenu -> addSeparator(); networkMenu -> addAction(networkPrint); networkMenu -> addSeparator(); networkMenu -> addAction(networkClose); networkMenu -> addAction(networkQuit); /** menuBar entry editMenu */ editMenu = menuBar()->addMenu(tr("&Edit")); editMenu -> addAction (editRelationPreviousAct); editMenu -> addAction (editRelationNextAct); editMenu -> addAction (editRelationAddAct); editMenu -> addAction (editRelationRenameAct); editMenu -> addSeparator(); editMenu -> addAction ( zoomInAct ); editMenu -> addAction ( zoomOutAct ); editMenu -> addSeparator(); editMenu -> addAction ( editRotateLeftAct ); editMenu -> addAction ( editRotateRightAct ); editMenu -> addSeparator(); editMenu -> addAction (editResetSlidersAct ); editMenu -> addSeparator(); editNodeMenu = new QMenu(tr("Nodes...")); editNodeMenu -> setIcon(QIcon(":/images/node.png")); editMenu -> addMenu ( editNodeMenu ); editNodeMenu -> addAction (editNodeSelectAllAct); editNodeMenu -> addAction (editNodeSelectNoneAct); editNodeMenu -> addSeparator(); editNodeMenu -> addAction (editNodeFindAct); editNodeMenu -> addAction (editNodeAddAct); editNodeMenu -> addAction (editNodeRemoveAct); editNodeMenu -> addSeparator(); editNodeMenu -> addAction (editNodePropertiesAct); editNodeMenu -> addSeparator(); editNodeMenu -> addAction (editNodeSelectedToCliqueAct); editNodeMenu -> addAction (editNodeSelectedToStarAct); editNodeMenu -> addAction (editNodeSelectedToCycleAct); editNodeMenu -> addAction (editNodeSelectedToLineAct); editNodeMenu -> addSeparator(); editNodeMenu -> addAction (editNodeColorAll); editNodeMenu -> addAction (editNodeSizeAllAct); editNodeMenu -> addAction (editNodeShapeAll); editNodeMenu -> addSeparator(); editNodeMenu -> addAction (editNodeNumbersSizeAct); editNodeMenu -> addAction (editNodeNumbersColorAct); editNodeMenu -> addSeparator(); editNodeMenu -> addAction (editNodeLabelsSizeAct); editNodeMenu -> addAction (editNodeLabelsColorAct); editEdgeMenu = new QMenu(tr("Edges...")); editEdgeMenu -> setIcon(QIcon(":/images/line.png")); editMenu-> addMenu (editEdgeMenu); editEdgeMenu -> addAction(editEdgeAddAct); editEdgeMenu -> addAction(editEdgeRemoveAct); editEdgeMenu -> addSeparator(); editEdgeMenu -> addAction (editEdgeSymmetrizeAllAct); editEdgeMenu -> addAction (editEdgeSymmetrizeStrongTiesAct); editEdgeMenu -> addAction (editEdgesCocitationAct); editEdgeMenu -> addAction (editEdgeUndirectedAllAct); editEdgeMenu -> addSeparator(); editEdgeMenu -> addAction(editEdgeLabelAct); editEdgeMenu -> addAction(editEdgeColorAct); editEdgeMenu -> addAction(editEdgeWeightAct); editEdgeMenu -> addSeparator(); editEdgeMenu -> addAction (editEdgeColorAllAct); // transformNodes2EdgesAct -> addTo (editMenu); editMenu ->addSeparator(); filterMenu = new QMenu ( tr("Filter...")); filterMenu -> setIcon(QIcon(":/images/filter.png")); editMenu ->addMenu(filterMenu); filterMenu -> addAction(filterNodesAct ); filterMenu -> addAction(editFilterNodesIsolatesAct ); filterMenu -> addAction(editFilterEdgesByWeightAct ); filterMenu -> addAction(editFilterEdgesUnilateralAct); /** menuBar entry: analyze menu */ analysisMenu = menuBar()->addMenu(tr("&Analyze")); matrixMenu = new QMenu(tr("Adjacency Matrix and Matrices...")); matrixMenu -> setIcon(QIcon(":/images/sm.png")); analysisMenu -> addMenu (matrixMenu); matrixMenu -> addAction (networkViewSociomatrixAct); matrixMenu -> addAction (networkViewSociomatrixPlotAct); matrixMenu -> addSeparator(); matrixMenu -> addAction (analyzeMatrixAdjInvertAct); matrixMenu -> addSeparator(); matrixMenu -> addAction(analyzeMatrixAdjTransposeAct); matrixMenu -> addSeparator(); matrixMenu -> addAction(analyzeMatrixAdjCocitationAct); matrixMenu -> addSeparator(); matrixMenu -> addAction (analyzeMatrixDegreeAct); matrixMenu -> addAction (analyzeMatrixLaplacianAct); // analysisMenu -> addAction (netDensity); analysisMenu -> addSeparator(); cohesionMenu = new QMenu(tr("Cohesion...")); cohesionMenu -> setIcon(QIcon(":/images/distances.png")); analysisMenu -> addMenu(cohesionMenu); cohesionMenu -> addAction (analyzeGraphSymmetryAct); cohesionMenu -> addSection("Graph distances"); cohesionMenu -> addAction (analyzeGraphDistanceAct); cohesionMenu -> addAction (averGraphDistanceAct); cohesionMenu -> addSeparator(); cohesionMenu -> addAction (analyzeMatrixDistancesGeodesicAct); cohesionMenu -> addAction (analyzeMatrixGeodesicsAct); cohesionMenu -> addSeparator(); cohesionMenu -> addAction (analyzeGraphEccentricityAct); cohesionMenu -> addAction (analyzeGraphDiameterAct); cohesionMenu -> addSeparator(); cohesionMenu -> addAction(analyzeGraphConnectednessAct); cohesionMenu -> addSeparator(); cohesionMenu -> addAction (analyzeGraphWalksAct); cohesionMenu -> addAction (analyzeGraphWalksTotalAct); cohesionMenu -> addSeparator(); cohesionMenu -> addAction (analyzeMatrixReachabilityAct); cohesionMenu -> addSeparator(); cohesionMenu -> addAction (clusteringCoefAct); analysisMenu->addSeparator(); // CENTRALITIES centrlMenu = new QMenu(tr("Centrality and Prestige indices...")); centrlMenu -> setIcon(QIcon(":/images/centrality.png")); analysisMenu->addMenu(centrlMenu); centrlMenu -> addSection(QIcon(":/images/centrality.png"), tr("Centrality")); centrlMenu -> addAction (cDegreeAct); centrlMenu -> addAction (cClosenessAct); centrlMenu -> addAction (cInfluenceRangeClosenessAct); centrlMenu -> addAction (cBetweennessAct); centrlMenu -> addAction (cStressAct); centrlMenu -> addAction (cEccentAct); centrlMenu -> addAction (cPowerAct); centrlMenu -> addAction (cInformationAct); centrlMenu -> addAction (cEigenvectorAct); centrlMenu -> addSection(QIcon(":/images/prestige.png"), tr("Prestige")); centrlMenu -> addAction (cInDegreeAct); centrlMenu -> addAction (cPageRankAct); centrlMenu -> addAction (cProximityPrestigeAct); analysisMenu -> addSeparator(); // COMMUNITIES & SUBGROUPS communitiesMenu = new QMenu(tr("Communities and Subgroups...")); communitiesMenu -> setIcon(QIcon(":/images/clustering.png")); analysisMenu -> addMenu(communitiesMenu); communitiesMenu -> addAction (analyzeCommunitiesCliquesAct); communitiesMenu -> addSeparator(); communitiesMenu -> addAction (analyzeCommunitiesTriadCensusAct); analysisMenu->addSeparator(); // STRUCTURAL EQUIVALENCE strEquivalenceMenu = new QMenu(tr("Structural Equivalence...")); strEquivalenceMenu -> setIcon(QIcon(":/images/similarity.png")); analysisMenu -> addMenu (strEquivalenceMenu); strEquivalenceMenu -> addAction (analyzeStrEquivalencePearsonAct); strEquivalenceMenu -> addAction(analyzeStrEquivalenceMatchesAct); strEquivalenceMenu -> addSeparator(); strEquivalenceMenu -> addAction (analyzeStrEquivalenceTieProfileDissimilaritiesAct); strEquivalenceMenu -> addSeparator(); strEquivalenceMenu -> addAction (analyzeStrEquivalenceClusteringHierarchicalAct); /** menuBar entry layoutMenu */ layoutMenu = menuBar()->addMenu(tr("&Layout")); // colorationMenu = new QPopupMenu(); // layoutMenu -> insertItem (tr("Colorization"), colorationMenu); // strongColorationAct -> addTo(colorationMenu); // regularColorationAct-> addTo(colorationMenu); // layoutMenu->insertSeparator(); randomLayoutMenu = new QMenu(tr("Random...")); layoutMenu -> addMenu (randomLayoutMenu ); randomLayoutMenu -> addAction(randLayoutAct); randomLayoutMenu -> addAction( randCircleLayoutAct ); layoutMenu->addSeparator(); circleLayoutMenu = new QMenu(tr("Circular by prominence index...")); circleLayoutMenu -> setIcon(QIcon(":/images/circular.png")); layoutMenu -> addMenu (circleLayoutMenu); circleLayoutMenu -> addAction (layoutCircular_DC_Act); circleLayoutMenu -> addAction (layoutCircular_CC_Act); circleLayoutMenu -> addAction (layoutCircular_IRCC_Act); circleLayoutMenu -> addAction (layoutCircular_BC_Act); circleLayoutMenu -> addAction (layoutCircular_SC_Act); circleLayoutMenu -> addAction (layoutCircular_EC_Act); circleLayoutMenu -> addAction (layoutCircular_PC_Act); circleLayoutMenu -> addAction (layoutCircular_IC_Act); circleLayoutMenu -> addAction (layoutCircular_DP_Act); circleLayoutMenu -> addAction (layoutCircular_PRP_Act); circleLayoutMenu -> addAction (layoutCircular_PP_Act); levelLayoutMenu = new QMenu (tr("On levels by prominence index...")); levelLayoutMenu -> setIcon(QIcon(":/images/net3.png")); layoutMenu -> addMenu (levelLayoutMenu); levelLayoutMenu -> addAction (layoutLevel_DC_Act); levelLayoutMenu -> addAction (layoutLevel_CC_Act); levelLayoutMenu -> addAction (layoutLevel_IRCC_Act); levelLayoutMenu -> addAction (layoutLevel_BC_Act); levelLayoutMenu -> addAction (layoutLevel_SC_Act); levelLayoutMenu -> addAction (layoutLevel_EC_Act); levelLayoutMenu -> addAction (layoutLevel_PC_Act); levelLayoutMenu -> addAction (layoutLevel_IC_Act); levelLayoutMenu -> addAction (layoutLevel_DP_Act); levelLayoutMenu -> addAction (layoutLevel_PRP_Act); levelLayoutMenu -> addAction (layoutLevel_PP_Act); layoutMenu->addSeparator(); physicalLayoutMenu = new QMenu (tr("Force-Directed...")); physicalLayoutMenu -> setIcon(QIcon(":/images/force.png")); layoutMenu -> addMenu (physicalLayoutMenu); physicalLayoutMenu -> addAction (springLayoutAct); physicalLayoutMenu -> addAction (FRLayoutAct); layoutMenu->addSeparator(); layoutMenu->addAction(nodeSizesByOutDegreeAct); layoutMenu->addAction(nodeSizesByInDegreeAct); layoutMenu->addSeparator(); layoutMenu -> addAction (layoutGuidesAct); /** menuBar entry optionsMenu */ optionsMenu = menuBar()->addMenu(tr("&Options")); nodeOptionsMenu=new QMenu(tr("Nodes...")); nodeOptionsMenu -> setIcon(QIcon(":/images/nodes.png")); optionsMenu -> addMenu (nodeOptionsMenu); nodeOptionsMenu -> addAction (optionsNodeNumbersVisibilityAct); nodeOptionsMenu -> addAction (optionsNodeLabelsVisibilityAct); nodeOptionsMenu -> addAction (optionsNodeNumbersInsideAct); edgeOptionsMenu=new QMenu(tr("Edges...")); edgeOptionsMenu -> setIcon(QIcon(":/images/line.png")); optionsMenu -> addMenu (edgeOptionsMenu); edgeOptionsMenu -> addAction (optionsEdgesVisibilityAct); edgeOptionsMenu -> addSeparator(); edgeOptionsMenu -> addAction (optionsEdgeWeightNumbersAct); edgeOptionsMenu -> addAction (considerEdgeWeightsAct); edgeOptionsMenu -> addAction (optionsEdgeThicknessPerWeightAct); edgeOptionsMenu -> addSeparator(); edgeOptionsMenu -> addAction (optionsEdgeLabelsAct); edgeOptionsMenu -> addSeparator(); edgeOptionsMenu -> addAction (optionsEdgeArrowsAct ); edgeOptionsMenu -> addSeparator(); edgeOptionsMenu -> addAction (drawEdgesBezier); viewOptionsMenu = new QMenu (tr("&View...")); viewOptionsMenu -> setIcon(QIcon(":/images/view.png")); optionsMenu -> addMenu (viewOptionsMenu); viewOptionsMenu -> addAction (changeBackColorAct); viewOptionsMenu -> addAction (backgroundImageAct); optionsMenu -> addSeparator(); optionsMenu -> addAction (openSettingsAct); /** menuBar entry helpMenu */ helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu -> addAction (helpApp); helpMenu -> addAction (tipsApp); helpMenu -> addSeparator(); helpMenu -> addAction (helpCheckUpdatesApp); helpMenu -> addSeparator(); helpMenu-> addAction (helpAboutApp); helpMenu-> addAction (helpAboutQt); } /** Initializes the toolbar */ void MainWindow::initToolBar(){ toolBar = addToolBar("operations"); toolBar -> addAction (networkNew); toolBar -> addAction (networkOpen); toolBar -> addAction (networkSave); toolBar -> addAction (networkPrint); toolBar -> addSeparator(); QLabel *labelRotateSpinBox= new QLabel; labelRotateSpinBox ->setText(tr("Rotation:")); toolBar -> addSeparator(); //Create relation select widget QLabel *labelRelationSelect= new QLabel; labelRelationSelect ->setText(tr("Relations:")); toolBar -> addWidget (labelRelationSelect); toolBar -> addAction (editRelationPreviousAct); editRelationChangeCombo = new QComboBox; editRelationChangeCombo ->setEditable(true); editRelationChangeCombo ->setInsertPolicy(QComboBox::InsertAtCurrent); editRelationChangeCombo->setMinimumWidth(180); editRelationChangeCombo->setCurrentIndex(0); editRelationChangeCombo->setToolTip( tr("Current relation. To rename it, write new name and press Enter.")); editRelationChangeCombo->setStatusTip( tr("Name of the current relation. " "To rename it, write a new name and press Enter. To select another relation use Down arrow")); editRelationChangeCombo->setWhatsThis( tr("Relations combo\n\n" "This combo box displays the current relation. \n" "To rename the current relation, write a new name and press Enter. " "To select another relation (if any), click the Down arrow.")); toolBar -> addWidget(editRelationChangeCombo); toolBar -> addAction (editRelationNextAct); toolBar -> addAction (editRelationAddAct); toolBar -> addSeparator(); QLabel *labelEditNodes= new QLabel; labelEditNodes ->setText(tr("Nodes:")); toolBar -> addWidget (labelEditNodes); toolBar -> addAction (editNodeAddAct); toolBar -> addAction (editNodeRemoveAct); toolBar -> addAction (editNodeFindAct); toolBar -> addAction(editNodePropertiesAct ); toolBar -> addSeparator(); QLabel *labelEditEdges= new QLabel; labelEditEdges ->setText(tr("Edges:")); toolBar -> addWidget (labelEditEdges); toolBar -> addAction (editEdgeAddAct); toolBar -> addAction (editEdgeRemoveAct); toolBar -> addAction (editFilterEdgesByWeightAct); toolBar -> addSeparator(); QLabel *labelApplicationIcons = new QLabel; labelApplicationIcons ->setText(tr("Settings:")); toolBar -> addWidget(labelApplicationIcons); toolBar -> addAction(openSettingsAct); toolBar -> addSeparator(); toolBar -> addAction ( QWhatsThis::createAction (this)); toolBar -> setIconSize(QSize(16,16)); } /** * @brief MainWindow::initToolBox * Creates a dock widget for instant menu access */ void MainWindow::initToolBox(){ /* * create widgets for the Controls Tab */ // create 4 buttons for the Edit groupbox // editNodeAddBt= new QPushButton(QIcon(":/images/add.png"),tr("&Add Node")); // editNodeAddBt->setFocusPolicy(Qt::NoFocus); // editNodeAddBt->setMinimumWidth(100); // editNodeAddBt->setStatusTip( tr("Add a new node to the network.") ) ; // editNodeAddBt->setToolTip( // tr("Add a new node to the network (Ctrl+.). \n\n" // "You can also create a new node \n" // "in a specific position by double-clicking.") // ); // editNodeAddBt->setWhatsThis( // tr("Add new node\n\n" // "Adds a new node to the network (Ctrl+.). \n\n" // "Alternately, you can create a new node " // "in a specific position by double-clicking " // "on that spot of the canvas.") // ); // removeNodeBt= new QPushButton(QIcon(":/images/remove.png"),tr("&Remove Node")); // removeNodeBt->setFocusPolicy(Qt::NoFocus); // removeNodeBt->setMinimumWidth(100); // removeNodeBt->setStatusTip( tr("Remove a node from the network. ") ); // removeNodeBt->setToolTip( // tr("Remove a node from the network (Ctrl+Alt+.). ") // ); // removeNodeBt->setWhatsThis( // tr("Remove node\n\n" // "Removes a node from the network (Ctrl+Alt+.). \n\n" // "Alternately, you can remove a node " // "by right-clicking on it.") // ); // editEdgeAddBt= new QPushButton(QIcon(":/images/connect.png"),tr("Add &Edge")); // editEdgeAddBt->setFocusPolicy(Qt::NoFocus); // editEdgeAddBt->setMinimumWidth(100); // editEdgeAddBt->setStatusTip( // tr("Add a new Edge from a node to another. ") // ); // editEdgeAddBt->setToolTip( // tr("Add a new Edge from a node to another (Ctrl+/).\n\n" // "You can also create an edge between two nodes \n" // "by double-clicking or middle-clicking on them consecutively.") // ); // editEdgeAddBt->setWhatsThis( // tr("Add edge\n\n" // "Adds a new Edge from a node to another (Ctrl+/).\n\n" // "Alternately, you can create a new edge between two nodes " // "by double-clicking or middle-clicking on them consecutively.") // ); // editEdgeRemoveBt= new QPushButton(QIcon(":/images/disconnect.png"),tr("Remove Edge")); // editEdgeRemoveBt->setFocusPolicy(Qt::NoFocus); // editEdgeRemoveBt->setMinimumWidth(100); // editEdgeRemoveBt->setStatusTip( tr("Remove an Edge from the network ") ); // editEdgeRemoveBt->setToolTip( // tr("Remove an Edge from the network (Ctrl+Alt+/)" // ) // ); // editEdgeRemoveBt->setWhatsThis( // tr("Remove edge\n\n" // "Removes an Edge from the network (Ctrl+Alt+/).\n\n" // "Alternately, you can remove an Edge " // "by right-clicking on it." // ) // ); QLabel *toolBoxEditNodeSubgraphSelectLabel = new QLabel; toolBoxEditNodeSubgraphSelectLabel->setText(tr("Selection Subgraph:")); toolBoxEditNodeSubgraphSelectLabel->setMinimumWidth(115); toolBoxEditNodeSubgraphSelect = new QComboBox; toolBoxEditNodeSubgraphSelect->setStatusTip( tr("Create a basic subgraph with selected nodes.")); toolBoxEditNodeSubgraphSelect->setToolTip( tr("Create a basic subgraph (star, clique, line, etc) " "with selected nodes. \n" "There must be some nodes selected!")); toolBoxEditNodeSubgraphSelect->setWhatsThis( tr("Selection Subgraph\n\n" "Creates basic subgraphs with all selected nodes: star, clique, line, etc.")); QStringList editNodeSubgraphCommands; editNodeSubgraphCommands << "Select" << "Clique" << "Star" << "Cycle" << "Line"; toolBoxEditNodeSubgraphSelect->addItems(editNodeSubgraphCommands); toolBoxEditNodeSubgraphSelect->setMinimumWidth(115); QLabel *toolBoxEdgeModeSelectLabel = new QLabel; toolBoxEdgeModeSelectLabel->setText(tr("Edge Mode:")); toolBoxEdgeModeSelectLabel->setMinimumWidth(115); toolBoxEditEdgeModeSelect = new QComboBox; toolBoxEditEdgeModeSelect->setStatusTip( tr("Select an edge creation mode: directed or undirected.")); toolBoxEditEdgeModeSelect->setToolTip( tr("Select an edge creation mode: directed or undirected.")); toolBoxEditEdgeModeSelect->setWhatsThis( tr("Edge mode\n\n" "Select what mode to use when creating new edges.")); QStringList edgeModeCommands; edgeModeCommands << "Directed" << "Undirected"; toolBoxEditEdgeModeSelect->addItems(edgeModeCommands); toolBoxEditEdgeModeSelect->setMinimumWidth(115); QLabel *toolBoxSymmetrizeSelectLabel = new QLabel; toolBoxSymmetrizeSelectLabel->setText(tr("Symmetrize:")); toolBoxSymmetrizeSelectLabel->setMinimumWidth(115); toolBoxEditEdgeSymmetrizeSelect = new QComboBox; toolBoxEditEdgeSymmetrizeSelect->setStatusTip( tr("Select a method to symmetrize the network, i.e. tranform all directed edges to undirected.")); toolBoxEditEdgeSymmetrizeSelect->setToolTip( tr("Select a method to symmetrize the network: \n\n" "Symmetrize Directed Edges:\n" "Makes all directed arcs in this relation reciprocal. \n" "That is, if there is an arc from node A to node B \n" "then a new arc from node B to node A is created \n" "with the same weight.\n\n" "Symmetrize Edges by examining Strong Ties:\n" "Creates a new symmetric relation by keeping strong ties only. \n" "In the new relation, a tie will exist between actor A and \n" "actor B only when both arcs A -> B and B -> A are present \n" "in the current or all relations. \n\n" "Symmetrize Edges by examining Cocitation:\n" "Creates a new symmetric relation by connecting actors \n" "that are cocitated by others. \n" "In the new relation, an edge will exist between actor i and \n" "actor j only if C(i,j) > 0, where C the Cocitation Matrix. " )); toolBoxEditEdgeSymmetrizeSelect->setWhatsThis( tr("Select a method to symmetrize the network: \n\n" "Symmetrize Directed Edges\n" "Makes all directed arcs in this relation reciprocal. " "That is, if there is an arc from node A to node B \n" "then a new arc from node B to node A is created \n" "with the same weight.\n\n" "Symmetrize Edges by examining Strong Ties:\n" "Creates a new symmetric relation by keeping strong ties only. " "That is, a strong tie exists between actor A and actor B " "only when both arcs A -> B and B -> A are present. " "If the network is multi-relational, it asks you whether " "ties in the current relation or all relations are to be considered. \n\n" "Symmetrize Edges by examining Cocitation:\n" "Creates a new symmetric relation by connecting actors " "that are cocitated by others. " "In the new relation, an edge will exist between actor i and " "actor j only if C(i,j) > 0, where C the Cocitation Matrix. " "Thus the actor pairs cited by more common neighbors will appear " "with a stronger tie between them than pairs those cited by fewer " "common neighbors. " )); QStringList symmetrizeCommands; symmetrizeCommands << "Select" << "Directed ties" << "Strong ties" << "Cocitation"; toolBoxEditEdgeSymmetrizeSelect->addItems(symmetrizeCommands); toolBoxEditEdgeSymmetrizeSelect->setMinimumWidth(115); //create a grid layout for Edit buttons QGridLayout *editNodesGrid = new QGridLayout; editNodesGrid -> addWidget(toolBoxEditNodeSubgraphSelectLabel, 0,0); editNodesGrid -> addWidget(toolBoxEditNodeSubgraphSelect, 0,1); QGroupBox *editNodesGroupBox= new QGroupBox(tr("Nodes")); editNodesGroupBox->setLayout(editNodesGrid); QGridLayout *editEdgeGrid = new QGridLayout; editEdgeGrid -> addWidget(toolBoxEdgeModeSelectLabel,0,0); editEdgeGrid -> addWidget(toolBoxEditEdgeModeSelect,0,1); editEdgeGrid -> addWidget(toolBoxSymmetrizeSelectLabel,1,0); editEdgeGrid -> addWidget(toolBoxEditEdgeSymmetrizeSelect,1,1); QGroupBox *editEdgeGroupBox= new QGroupBox(tr("Edges")); editEdgeGroupBox->setLayout(editEdgeGrid); QGridLayout *EditGrid = new QGridLayout; EditGrid -> addWidget(editNodesGroupBox, 0,0,1,2); EditGrid -> addWidget(editEdgeGroupBox,1,0,1,2); EditGrid -> setSpacing(5); EditGrid -> setContentsMargins(5, 5, 5, 5); //create a groupbox "Edit" - Inside, display the grid layout of widgets QGroupBox *editGroupBox= new QGroupBox(tr("Edit")); editGroupBox->setLayout(EditGrid); editGroupBox->setMaximumWidth(280); editGroupBox->setMinimumHeight(100); //create widgets for the "Analysis" box QLabel *toolBoxAnalysisMatricesSelectLabel = new QLabel; toolBoxAnalysisMatricesSelectLabel->setText(tr("Matrix:")); toolBoxAnalysisMatricesSelectLabel->setMinimumWidth(115); toolBoxAnalysisMatricesSelect = new QComboBox; toolBoxAnalysisMatricesSelect -> setStatusTip( tr("Select which matrix to compute and display, based on the adjacency matrix of the current network.")); toolBoxAnalysisMatricesSelect -> setToolTip( tr("The adjacency matrix and other matrices based on the adjacency \n" "matrix of the current network, i.e. Cocitation, Degree Matrix etc.")); toolBoxAnalysisMatricesSelect -> setWhatsThis( tr("Analyze Matrices\n\n" "Compute and display matrices based on the adjacency matrix of the current network.")); QStringList graphMatricesList; graphMatricesList << "Select" << "Adjacency" << "Adjacency Plot" << "Adjacency Inverse" << "Adjacency Transpose" << "Cocitation Matrix" << "Degree Matrix" << "Laplacian Matrix"; toolBoxAnalysisMatricesSelect->addItems(graphMatricesList); toolBoxAnalysisMatricesSelect->setMinimumWidth(115); QLabel *toolBoxAnalysisCohesionSelectLabel = new QLabel; toolBoxAnalysisCohesionSelectLabel->setText(tr("Cohesion:")); toolBoxAnalysisCohesionSelectLabel->setMinimumWidth(115); toolBoxAnalysisCohesionSelect = new QComboBox; toolBoxAnalysisCohesionSelect -> setStatusTip( tr("Select a graph-theoretic metric to compute, i.e. distances, walks, graph diameter, eccentricity.")); toolBoxAnalysisCohesionSelect -> setToolTip( tr("Basic graph-theoretic metrics, such as distances, walks, \n" "graph diameter, eccentricity, clustering coefficient, etc.")); toolBoxAnalysisCohesionSelect -> setWhatsThis( tr("Analyze Cohesion\n\n" "Compute basic graph-theoretic metrics, i.e. distances, walks, graph diameter, eccentricity.")); QStringList graphPropertiesList; graphPropertiesList << "Select" << "Symmetry" << "Distance" << "Average Distance" << "Distances Matrix" << "Geodesics Matrix" << "Eccentricity" << "Diameter" << "Connectedness" << "Walks of given length" << "Total Walks" << "Reachability Matrix" << "Clustering Coefficient"; toolBoxAnalysisCohesionSelect->addItems(graphPropertiesList); toolBoxAnalysisCohesionSelect->setMinimumWidth(115); QLabel *toolBoxAnalysisProminenceSelectLabel = new QLabel; toolBoxAnalysisProminenceSelectLabel->setText(tr("Prominence:")); toolBoxAnalysisProminenceSelectLabel->setMinimumWidth(115); toolBoxAnalysisProminenceSelect = new QComboBox; toolBoxAnalysisProminenceSelect -> setStatusTip( tr("Select a prominence metric to compute for each actor and the whole network. ") ); toolBoxAnalysisProminenceSelect -> setToolTip( tr("Metrics to understand how 'prominent' or important each \n" "actor (node) is inside the network, i.e.\n Betweeness Centrality, \n" "Eigenvector Centrality, PageRank etc.") ); toolBoxAnalysisProminenceSelect -> setWhatsThis( tr("Analyze Prominence\n\n" "Computes various metrics to see how 'prominent' or " "important each actor (node) is inside the network.\n\n" "Centrality metrics quantify how central is each node by examining " "its ties and its geodesic distances (shortest path lengths) to other nodes. " "Most Centrality indices were designed for undirected graphs.\n\n" "Prestige indices focus on \"choices received\" to a node. " "These indices measure the nominations or ties to each node from all others (or inLinks). " "Prestige indices are suitable (and can be calculated only) on directed graphs.") ); QStringList prominenceCommands; prominenceCommands << "Select" << "Degree Centr." << "Closeness Centr." << "IR Closeness Centr." << "Betweenness Centr." << "Stress Centr." << "Eccentricity Centr." << "Power Centr." << "Information Centr." << "Eigenvector Centr" << "Degree Prestige" << "PageRank Prestige" << "Proximity Prestige"; toolBoxAnalysisProminenceSelect->addItems(prominenceCommands); toolBoxAnalysisProminenceSelect->setMinimumWidth(115); QLabel *toolBoxAnalysisCommunitiesSelectLabel = new QLabel; toolBoxAnalysisCommunitiesSelectLabel->setText(tr("Communities:")); toolBoxAnalysisCommunitiesSelectLabel->setMinimumWidth(115); toolBoxAnalysisCommunitiesSelect = new QComboBox; toolBoxAnalysisCommunitiesSelect->setStatusTip( tr("Select a community detection metric / cohesive subgroup algorithm, i.e. cliques, triad census etc.")); toolBoxAnalysisCommunitiesSelect->setToolTip( tr("Community detection metrics and cohesive subgroup algorithms, \n" "i.e. cliques, triad census etc.")); toolBoxAnalysisCommunitiesSelect->setWhatsThis( tr("Analyze Communities\n\n" "Community detection metrics and cohesive subgroup algorithms, " "(i.e. cliques, triad census etc), to identify " "meaningful subgraphs in the graph." "For instance, select cliques to count and identify " "all maximal cliques of actors in the network. ")); QStringList communitiesCommands; communitiesCommands << "Select" << "Cliques" << "Triad Census"; toolBoxAnalysisCommunitiesSelect->addItems(communitiesCommands); toolBoxAnalysisCommunitiesSelect->setMinimumWidth(115); QLabel *toolBoxAnalysisStrEquivalenceSelectLabel = new QLabel; toolBoxAnalysisStrEquivalenceSelectLabel->setText(tr("Equivalence:")); toolBoxAnalysisStrEquivalenceSelectLabel->setMinimumWidth(115); toolBoxAnalysisStrEquivalenceSelect = new QComboBox; toolBoxAnalysisStrEquivalenceSelect->setStatusTip( tr("Select a metric to measure structural equivalence, " "i.e. Pearson Coefficients, tie profile similarities, " "hierarchical clustering, etc.")); toolBoxAnalysisStrEquivalenceSelect->setToolTip( tr("Structural equivalence measures and visualization algorithms, \n" "i.e. Pearson Coefficients, tie profile similarities, \n" "hierarchical clustering")); toolBoxAnalysisStrEquivalenceSelect->setWhatsThis( tr("Analyze Structural Equivalence\\n\n" "Structural equivalence measures and visualization algorithms, " "i.e. Pearson Coefficients, tie profile similarities, " "hierarchical clustering ")); QStringList connectivityCommands; connectivityCommands << "Select" << "Pearson Coefficients" << "Similarities" << "Dissimilarities" << "Hierarchical Clustering"; toolBoxAnalysisStrEquivalenceSelect->addItems(connectivityCommands); toolBoxAnalysisStrEquivalenceSelect->setMinimumWidth(115); //create layout for analysis options QGridLayout *analysisGrid = new QGridLayout(); analysisGrid -> addWidget(toolBoxAnalysisMatricesSelectLabel, 0,0); analysisGrid -> addWidget(toolBoxAnalysisMatricesSelect, 0,1); analysisGrid -> addWidget(toolBoxAnalysisCohesionSelectLabel, 1,0); analysisGrid -> addWidget(toolBoxAnalysisCohesionSelect, 1,1); analysisGrid -> addWidget(toolBoxAnalysisProminenceSelectLabel, 2,0); analysisGrid -> addWidget(toolBoxAnalysisProminenceSelect, 2,1); analysisGrid -> addWidget(toolBoxAnalysisCommunitiesSelectLabel, 3,0); analysisGrid -> addWidget(toolBoxAnalysisCommunitiesSelect, 3,1); analysisGrid -> addWidget(toolBoxAnalysisStrEquivalenceSelectLabel, 4,0); analysisGrid -> addWidget(toolBoxAnalysisStrEquivalenceSelect, 4,1); analysisGrid -> setSpacing(5); analysisGrid -> setContentsMargins(15, 5, 15, 5); //create a box and set the above layout inside QGroupBox *analysisBox= new QGroupBox(tr("Analyze")); analysisBox->setMinimumHeight(170); analysisBox->setMaximumWidth(280); analysisBox->setLayout (analysisGrid ); //create widgets for the "Visualization By Index" box QLabel *toolBoxLayoutByIndexSelectLabel = new QLabel; toolBoxLayoutByIndexSelectLabel->setText(tr("Index:")); toolBoxLayoutByIndexSelectLabel->setMinimumWidth(110); toolBoxLayoutByIndexSelect = new QComboBox; toolBoxLayoutByIndexSelect->setStatusTip(tr("Select a prominence-based layout model")); toolBoxLayoutByIndexSelect->setToolTip(tr("Apply a prominence-based layout model")); toolBoxLayoutByIndexSelect->setWhatsThis( tr("Visualize by prominence index\n\n" "Apply a prominence-based layout model to the network. \n" "For instance, you can apply a degree centrality layout. " "For each prominence index, you can select a circular or level layout type.")); QStringList indicesList; indicesList << "None" << "Random" << "Degree Centr." << "Closeness Centr." << "IR Closeness Centr." << "Betweenness Centr." << "Stress Centr." << "Eccentricity Centr." << "Power Centr." << "Information Centr." << "Eigenvector Centr." << "Degree Prestige" << "PageRank Prestige" << "Proximity Prestige"; toolBoxLayoutByIndexSelect->addItems(indicesList); toolBoxLayoutByIndexSelect->setMinimumHeight(20); toolBoxLayoutByIndexSelect->setMinimumWidth(120); QLabel *toolBoxLayoutByIndexTypeLabel = new QLabel; toolBoxLayoutByIndexTypeLabel->setText(tr("Layout Type:")); toolBoxLayoutByIndexTypeLabel->setMinimumWidth(10); toolBoxLayoutByIndexTypeSelect = new QComboBox; toolBoxLayoutByIndexTypeSelect->setStatusTip( tr("Select layout type for the selected model")); toolBoxLayoutByIndexTypeSelect->setToolTip( tr("Select circular or level layout type (you must select an index above)")); toolBoxLayoutByIndexTypeSelect->setWhatsThis( tr("Layout Type\n\n" "Select a layout type (circular or level) for the selected prominence-based model " "you want to apply to the network.")); QStringList layoutTypes; layoutTypes << "Circular" << "On Levels" << "Nodal size"; toolBoxLayoutByIndexTypeSelect->addItems(layoutTypes); toolBoxLayoutByIndexTypeSelect->setMinimumHeight(20); toolBoxLayoutByIndexTypeSelect->setMinimumWidth(120); toolBoxLayoutByIndexButton = new QPushButton(tr("Apply")); toolBoxLayoutByIndexButton->setFocusPolicy(Qt::NoFocus); toolBoxLayoutByIndexButton->setMinimumHeight(20); toolBoxLayoutByIndexButton->setMaximumWidth(60); //create layout for visualisation by index options QGridLayout *layoutByIndexGrid = new QGridLayout(); layoutByIndexGrid -> addWidget(toolBoxLayoutByIndexSelectLabel, 0,0); layoutByIndexGrid -> addWidget(toolBoxLayoutByIndexSelect, 0,1); layoutByIndexGrid -> addWidget(toolBoxLayoutByIndexTypeLabel, 1,0); layoutByIndexGrid -> addWidget(toolBoxLayoutByIndexTypeSelect, 1,1); layoutByIndexGrid -> addWidget(toolBoxLayoutByIndexButton, 2,1); layoutByIndexGrid -> setSpacing(5); layoutByIndexGrid -> setContentsMargins(5, 5, 5, 5); //create a box and set the above layout inside QGroupBox *layoutByIndexBox= new QGroupBox(tr("By Prominence Index")); layoutByIndexBox->setMinimumHeight(120); layoutByIndexBox->setLayout (layoutByIndexGrid ); // create widgets for the "Force-Directed Models" Box QLabel *toolBoxLayoutForceDirectedSelectLabel = new QLabel; toolBoxLayoutForceDirectedSelectLabel->setText(tr("Model:")); toolBoxLayoutForceDirectedSelectLabel->setMinimumWidth(110); toolBoxLayoutForceDirectedSelect = new QComboBox; QStringList modelsList; modelsList << tr("None") << tr("Spring Embedder (Eades)") << tr("Fruchterman-Reingold"); // << tr("Kamada-Kawai") ; toolBoxLayoutForceDirectedSelect->addItems(modelsList); toolBoxLayoutForceDirectedSelect->setMinimumHeight(20); toolBoxLayoutForceDirectedSelect->setMinimumWidth(120); toolBoxLayoutForceDirectedSelect->setStatusTip ( tr("Select a Force-Directed layout model. ")); toolBoxLayoutForceDirectedSelect->setToolTip ( tr("Select a Force-Directed layout model to embed to the network.\n" "Available models: \n\n" "Eades: A spring-gravitational model, where connected nodes \n" "attract each other and all nodes repel all other non-adjacent \n" "nodes. \n\n" "Fruchterman-Reingold: Again adjacent vertices attract each \n" "each other but, unlike Eades Spring Embedder, all vertices \n" "repel each other.\n\n" "Kamada-Kawai\n" "Another spring embedder model, where the optimal layout is \n" "that of minimum total spring energy, which is computed as \n" "the square summation of the differences between desirable \n" "distances and real ones for all pairs of vertices" ) ); toolBoxLayoutForceDirectedSelect->setWhatsThis( tr("Visualize by a Force-Directed layout model.\n\n" "Available models: \n\n" "Eades model\n" "A spring-gravitational model, where each node is " "regarded as physical object (ring) repeling all other non-adjacent" "nodes, while springs between connected nodes attract them. \n\n" "Fruchterman-Reingold\n" "In this model, the vertices behave as atomic particles " "or celestial bodies, exerting attractive and repulsive " "forces to each other. Again, only vertices that are " "neighbours attract each other but, unlike Eades Spring " "Embedder, all vertices repel each other.\n\n" "Kamada-Kawai\n" "Another variant of Eades model. In this the graph is considered to be a dynamic system " "where every two vertices are connected by a 'spring' of a " "desirable length, which corresponds to their graph theoretic " "distance. In this way, the optimal layout of the graph " "is the state with the minimum imbalance. The degree of " "imbalance is formulated as the total spring energy: " "the square summation of the differences between desirable " "distances and real ones for all pairs of vertices" ) ); toolBoxLayoutForceDirectedButton = new QPushButton(tr("Apply")); toolBoxLayoutForceDirectedButton->setFocusPolicy(Qt::NoFocus); toolBoxLayoutForceDirectedButton->setMinimumHeight(20); toolBoxLayoutForceDirectedButton->setMaximumWidth(60); //create layout for dynamic visualisation QGridLayout *layoutForceDirectedGrid = new QGridLayout(); layoutForceDirectedGrid -> addWidget(toolBoxLayoutForceDirectedSelectLabel, 0,0); layoutForceDirectedGrid -> addWidget(toolBoxLayoutForceDirectedSelect, 0,1); layoutForceDirectedGrid -> addWidget(toolBoxLayoutForceDirectedButton, 1,1); layoutForceDirectedGrid -> setSpacing(5); layoutForceDirectedGrid -> setContentsMargins(5,5, 5, 5); //create a box for dynamic layout options QGroupBox *layoutDynamicBox= new QGroupBox(tr("By Force-Directed Model")); layoutDynamicBox->setMinimumHeight(90); layoutDynamicBox->setLayout (layoutForceDirectedGrid ); //create widgets for additional visualization options box toolBoxNodeSizesByOutDegreeBx = new QCheckBox( tr("Node sizes by OutDegree") ); toolBoxNodeSizesByOutDegreeBx ->setEnabled(true); toolBoxNodeSizesByOutDegreeBx ->setStatusTip( tr("Enable to have all nodes resized so that their " "size reflect their out-degree.")); toolBoxNodeSizesByOutDegreeBx ->setToolTip( tr("If you enable this, all nodes will be resized " "so that their size reflect their out-degree. \n" "Nodes with more outbound directed edges will be bigger...")); toolBoxNodeSizesByInDegreeBx = new QCheckBox( tr("Node sizes by InDegree") ); toolBoxNodeSizesByInDegreeBx ->setEnabled(true); toolBoxNodeSizesByInDegreeBx ->setStatusTip( tr("Enable to have all nodes resized so that their " "size reflect their in-degree." ) ); toolBoxNodeSizesByInDegreeBx ->setToolTip( tr("If you enable this, all nodes will be resized " "so that their size reflect their in-degree. \n" "Nodes with more inbound directed edges them will be bigger...")); toolBoxLayoutGuidesBx = new QCheckBox( tr("Layout guidelines") ); toolBoxLayoutGuidesBx->setToolTip( tr("Toggle layout guidelines on or off.")); toolBoxLayoutGuidesBx ->setStatusTip(tr("Toggle layout guidelines on or off.")); toolBoxLayoutGuidesBx->setWhatsThis(tr("Layout Guidelines\n\n" "Layout Guidelines are circular or horizontal lines \n" "usually created when embedding prominence-based \n" "visualization models on the network.\n" "Disable this checkbox to hide guidelines")); toolBoxLayoutGuidesBx ->setEnabled(true); toolBoxLayoutGuidesBx ->setChecked(true); QGridLayout *layoutOptionsGrid = new QGridLayout(); layoutOptionsGrid -> addWidget(toolBoxNodeSizesByOutDegreeBx, 0,0); layoutOptionsGrid -> addWidget(toolBoxNodeSizesByInDegreeBx, 1,0); layoutOptionsGrid -> addWidget(toolBoxLayoutGuidesBx, 2,0); layoutOptionsGrid->setSpacing(5); layoutOptionsGrid->setContentsMargins(5, 5, 5, 5); //Box for additional visualization options QGroupBox *visualizeOptionsBox= new QGroupBox(tr("Options")); visualizeOptionsBox->setMinimumHeight(110); visualizeOptionsBox->setMaximumWidth(280); visualizeOptionsBox->setLayout (layoutOptionsGrid ); //Parent box with vertical layout for all layout/visualization boxes QVBoxLayout *visualizationBoxLayout = new QVBoxLayout; visualizationBoxLayout -> addWidget(layoutByIndexBox); visualizationBoxLayout -> addWidget(layoutDynamicBox); //visualizationBoxLayout -> addWidget(visualizeOptionsBox); QGroupBox *visualizationBox= new QGroupBox(tr("Visualize")); visualizationBox->setMaximumWidth(280); visualizationBox->setLayout (visualizationBoxLayout ); //Parent box with vertical layout for all boxes of Controls QGridLayout *editGrid = new QGridLayout; editGrid -> addWidget(editGroupBox, 0,0); editGrid -> addWidget(analysisBox, 1, 0); editGrid -> addWidget(visualizationBox, 2, 0); editGrid -> setRowStretch(3,1); //fix stretch //create a box with title leftPanel = new QGroupBox(tr("Control Panel")); leftPanel -> setLayout (editGrid); //create widgets for Properties/Statistics group/tab rightPanelNetworkTypeLabel = new QLabel; rightPanelNetworkTypeLabel-> setText ("Network Type: Undirected"); rightPanelNetworkTypeLabel->setStatusTip(tr("Directed data mode. Toggle the menu option Edit -> Edges -> Undirected Edges to change it")); rightPanelNetworkTypeLabel->setToolTip(tr("The loaded network, if any, is directed and \n" "any link you add between nodes will be a directed arc.\n" "If you want to work with undirected edges and/or \n" "transform the loaded network (if any) to undirected \n" "toggle the option Edit -> Edges -> Undirected \n" "or press CTRL+E+U")); rightPanelNetworkTypeLabel->setWhatsThis(tr("The loaded network, if any, is directed and \n" "any link you add between nodes will be a directed arc.\n" "If you want to work with undirected edges and/or \n" "transform the loaded network (if any) to undirected \n" "toggle the option Edit -> Edges -> Undirected \n" "or press CTRL+E+U")); QFont labelFont = rightPanelNetworkTypeLabel ->font(); labelFont.setWeight(QFont::Bold); rightPanelNetworkTypeLabel ->setFont(labelFont); rightPanelNetworkTypeLabel ->setFixedWidth(195); QLabel *rightPanelNodesLabel = new QLabel; rightPanelNodesLabel->setText(tr("Total Nodes")); rightPanelNodesLabel->setStatusTip(tr("The total number of actors (nodes or vertices) in the social network.")); rightPanelNodesLabel->setToolTip(tr("The total number of actors \n" "(nodes or vertices) in the social network.")); rightPanelNodesLCD=new QLCDNumber(7); rightPanelNodesLCD->setSegmentStyle(QLCDNumber::Flat); rightPanelNodesLCD->setStatusTip(tr("The total number of actors (nodes or vertices) in the social network.")); rightPanelNodesLCD->setToolTip(tr("This is the total number of actors \n" "(nodes or vertices) in the social network.")); rightPanelEdgesLabel = new QLabel; rightPanelEdgesLabel->setText(tr("Total Arcs")); rightPanelEdgesLabel->setStatusTip(tr("The total number of edges (links between actors) in the social network.")); rightPanelEdgesLabel->setToolTip(tr("This is the total number of edges \n" "(links between actors) in the social network.")); rightPanelEdgesLCD=new QLCDNumber(7); rightPanelEdgesLCD->setSegmentStyle(QLCDNumber::Flat); rightPanelEdgesLCD->setStatusTip(tr("The total number of directed edges in the social network.")); rightPanelEdgesLCD->setToolTip(tr("This is the total number of directed edges \n" "(links between actors) in the social network.")); QLabel *rightPanelDensityLabel = new QLabel; rightPanelDensityLabel->setText(tr("Density")); rightPanelDensityLabel->setToolTip(tr("The density of a social network is the ratio of existing \n" "edges to all possible edges ( n*(n-1) ) between nodes.")); rightPanelDensityLCD=new QLCDNumber(7); rightPanelDensityLCD->setSegmentStyle(QLCDNumber::Flat); rightPanelDensityLCD->setStatusTip(tr("The network density, the ratio of existing " "edges to all possible edges ( n*(n-1) ) between nodes.")); rightPanelDensityLCD->setToolTip(tr("This is the density of the network. \n" "The density of a network is the ratio of existing \n" "edges to all possible edges ( n*(n-1) ) between nodes.")); QLabel *verticalSpaceLabel1 = new QLabel; verticalSpaceLabel1 -> setText (""); QLabel *rightPanelSelectedHeaderLabel = new QLabel; rightPanelSelectedHeaderLabel-> setText (tr("Selection")); rightPanelSelectedHeaderLabel->setFont(labelFont); QLabel *rightPanelSelectedNodesLabel = new QLabel; rightPanelSelectedNodesLabel->setText(tr("Selected Nodes")); rightPanelSelectedNodesLabel->setStatusTip(tr("The number of selected nodes (vertices).")); rightPanelSelectedNodesLabel->setToolTip(tr("The number of selected nodes (vertices).")); rightPanelSelectedNodesLCD=new QLCDNumber(7); rightPanelSelectedNodesLCD->setSegmentStyle(QLCDNumber::Flat); rightPanelSelectedNodesLCD->setStatusTip(tr("The number of selected nodes (vertices).")); rightPanelSelectedNodesLCD->setToolTip(tr("The number of selected nodes (vertices).")); rightPanelSelectedEdgesLabel = new QLabel; rightPanelSelectedEdgesLabel->setText(tr("Selected Arcs")); rightPanelSelectedEdgesLabel->setStatusTip(tr("The number of selected edges.")); rightPanelSelectedEdgesLabel->setToolTip(tr("The number of selected edges.")); rightPanelSelectedEdgesLCD=new QLCDNumber(7); rightPanelSelectedEdgesLCD->setSegmentStyle(QLCDNumber::Flat); rightPanelSelectedEdgesLCD->setStatusTip(tr("The number of selected edges.")); rightPanelSelectedEdgesLCD->setToolTip(tr("The number of selected edges.")); QLabel *verticalSpaceLabel2 = new QLabel; verticalSpaceLabel2-> setText (""); QLabel *rightPanelClickedNodeHeaderLabel = new QLabel; rightPanelClickedNodeHeaderLabel-> setText (tr("Clicked Node")); rightPanelClickedNodeHeaderLabel->setFont(labelFont); QLabel *rightPanelClickedNodeLabel = new QLabel; rightPanelClickedNodeLabel -> setText (tr("Number:")); rightPanelClickedNodeLabel -> setToolTip (tr("The node number of the last clicked node.")); rightPanelClickedNodeLabel -> setStatusTip( tr("The node number of the last clicked node. Zero means no node clicked.")); rightPanelClickedNodeLCD =new QLCDNumber(5); rightPanelClickedNodeLCD ->setSegmentStyle(QLCDNumber::Flat); rightPanelClickedNodeLCD -> setToolTip (tr("This is the node number of the last clicked node. \n" "Becomes zero when you click on something other than a node.")); rightPanelClickedNodeLCD -> setStatusTip( tr("The node number of the last clicked node. Zero if you clicked something else.")); QLabel *rightPanelClickedNodeInDegreeLabel = new QLabel; rightPanelClickedNodeInDegreeLabel -> setText (tr("In-Degree:")); rightPanelClickedNodeInDegreeLabel -> setToolTip (tr("The inDegree of a node is the sum of all inbound edge weights.")); rightPanelClickedNodeInDegreeLabel -> setStatusTip (tr("The inDegree of a node is the sum of all inbound edge weights.")); rightPanelClickedNodeInDegreeLCD=new QLCDNumber(5); rightPanelClickedNodeInDegreeLCD -> setSegmentStyle(QLCDNumber::Flat); rightPanelClickedNodeInDegreeLCD -> setStatusTip (tr("The sum of all inbound edge weights of the last clicked node. " "Zero if you clicked something else.")); rightPanelClickedNodeInDegreeLCD -> setToolTip (tr("This is the sum of all inbound edge weights of last clicked node. \n" "Becomes zero when you click on something other than a node.")); QLabel *rightPanelClickedNodeOutDegreeLabel = new QLabel; rightPanelClickedNodeOutDegreeLabel -> setText (tr("Out-Degree:")); rightPanelClickedNodeOutDegreeLabel -> setToolTip (tr("The outDegree of a node is the sum of all outbound edge weights.")); rightPanelClickedNodeOutDegreeLabel -> setStatusTip (tr("The outDegree of a node is the sum of all outbound edge weights.")); rightPanelClickedNodeOutDegreeLCD=new QLCDNumber(5); rightPanelClickedNodeOutDegreeLCD -> setSegmentStyle(QLCDNumber::Flat); rightPanelClickedNodeOutDegreeLCD -> setStatusTip (tr("The sum of all outbound edge weights of the last clicked node. " "Zero if you clicked something else.")); rightPanelClickedNodeOutDegreeLCD -> setToolTip (tr("This is the sum of all outbound edge weights of the last clicked node. \n" "Becomes zero when you click on something other than a node.")); QLabel *rightPanelClickedNodeClucofLabel = new QLabel; rightPanelClickedNodeClucofLabel -> setText (tr("Clu.Coef.")); rightPanelClickedNodeClucofLabel -> setWhatsThis( tr("The Clustering Coefficient quantifies how close the clicked \n" "vertex and its neighbors are to being a clique. \n" "The value is the proportion of Edges between the vertices \n" "within the neighbourhood of the clicked vertex, \n" "divided by the number of Edges that could possibly exist " "between them. \n\n" "This value is automatically calculated only if vertices < 500.\n" "If your network is larger than 500 vertices, compute CluCof " "from the menu Analysis > Clustering Coefficient ")); rightPanelClickedNodeClucofLabel -> setToolTip ( tr("The Clustering Coefficient quantifies how close the clicked \n" "vertex and its neighbors are to being a clique. \n\n" "The value is the proportion of Edges between the vertices \n" "within the neighbourhood of the clicked vertex, \n" "divided by the number of Edges that could possibly exist \n" "between them. \n\n" "This value is automatically calculated only if vertices < 500.\n" "If your network is larger than 500 vertices, compute CluCof " "from the menu Analysis > Clustering Coefficient ")); rightPanelClickedNodeClucofLCD = new QLCDNumber(5); rightPanelClickedNodeClucofLCD -> setSegmentStyle(QLCDNumber::Flat); rightPanelClickedNodeClucofLCD -> setStatusTip( tr("The Clustering Coefficient of the last clicked node. " "Zero when you click on something else.")); rightPanelClickedNodeClucofLCD -> setWhatsThis( tr("The Clustering Coefficient of the active node. \n" "The Clustering Coefficient quantifies how close the clicked \n" "vertex and its neighbors are to being a clique. \n" "The value is the proportion of Edges between the vertices \n" "within the neighbourhood of the clicked vertex, \n" "divided by the number of Edges that could possibly exist " "between them. \n\n" "This value is automatically calculated only if vertices < 500.\n" "If your network is larger than 500 vertices, compute CluCof " "from the menu Analysis > Clustering Coefficient ")); rightPanelClickedNodeClucofLCD -> setToolTip ( tr("The Clustering Coefficient of the active node. \n" "The Clustering Coefficient quantifies how close the clicked \n" "vertex and its neighbors are to being a clique. \n" "The value is the proportion of Edges between the vertices \n" "within the neighbourhood of the clicked vertex, \n" "divided by the number of Edges that could possibly exist " "between them. \n\n" "This value is automatically calculated only if vertices < 500.\n" "If your network is larger than 500 vertices, compute CluCof \n" "from the menu Analysis > Clustering Coefficient ")); QLabel *verticalSpaceLabel3 = new QLabel; verticalSpaceLabel3-> setText (""); rightPanelClickedEdgeHeaderLabel = new QLabel; rightPanelClickedEdgeHeaderLabel-> setText (tr("Clicked Edge")); rightPanelClickedEdgeHeaderLabel->setFont(labelFont); QLabel *rightPanelClickedEdgeSourceLabel = new QLabel; rightPanelClickedEdgeSourceLabel -> setText (tr("Edge source:")); rightPanelClickedEdgeSourceLabel -> setToolTip (tr("The number of the last clicked edge source node.")); rightPanelClickedEdgeSourceLCD =new QLCDNumber(5); rightPanelClickedEdgeSourceLCD ->setSegmentStyle(QLCDNumber::Flat); rightPanelClickedEdgeSourceLCD -> setToolTip (tr("This is the node number of the last clicked edge source node. \n" "Becomes zero when you click on somethingto other than an edge")); rightPanelClickedEdgeSourceLCD -> setStatusTip (tr("The node number of the last clicked edge source node." "Zero when you click on something else.")); QLabel *rightPanelClickedEdgeTargetLabel = new QLabel; rightPanelClickedEdgeTargetLabel -> setText (tr("Edge target:")); rightPanelClickedEdgeTargetLabel -> setToolTip (tr("The number of the target node.")); rightPanelClickedEdgeTargetLCD =new QLCDNumber(5); rightPanelClickedEdgeTargetLCD ->setSegmentStyle(QLCDNumber::Flat); rightPanelClickedEdgeTargetLCD -> setToolTip (tr("This is the node number of the last clicked edge target node. \n" "Becomes zero when you click on something other than an edge")); rightPanelClickedEdgeTargetLCD -> setStatusTip (tr("The node number of the last clicked edge target node." "Zero when you click on something else.")); QLabel *rightPanelClickedEdgeWeightLabel = new QLabel; rightPanelClickedEdgeWeightLabel -> setText (tr("Edge weight:")); rightPanelClickedEdgeWeightLabel -> setToolTip (tr("The weight of the clicked edge.")); rightPanelClickedEdgeWeightLCD =new QLCDNumber(5); rightPanelClickedEdgeWeightLCD ->setSegmentStyle(QLCDNumber::Flat); rightPanelClickedEdgeWeightLCD -> setToolTip (tr("This is the weight of the last clicked edge. \n" "Becomes zero when you click on something other than an edge")); rightPanelClickedEdgeWeightLCD -> setStatusTip (tr("The weight of the last clicked edge. " "Zero when you click on something else.")); //create a grid layout QGridLayout *propertiesGrid = new QGridLayout(); propertiesGrid -> setColumnMinimumWidth(0, 10); propertiesGrid -> setColumnMinimumWidth(1, 10); propertiesGrid -> addWidget(rightPanelNetworkTypeLabel , 0,0); propertiesGrid -> addWidget(rightPanelNodesLabel, 1,0); propertiesGrid -> addWidget(rightPanelNodesLCD,1,1); propertiesGrid -> addWidget(rightPanelEdgesLabel, 2,0); propertiesGrid -> addWidget(rightPanelEdgesLCD,2,1); propertiesGrid -> addWidget(rightPanelDensityLabel, 3,0); propertiesGrid -> addWidget(rightPanelDensityLCD,3,1); propertiesGrid -> addWidget(verticalSpaceLabel1, 4,0); propertiesGrid -> addWidget(rightPanelSelectedHeaderLabel, 5,0,1,2); propertiesGrid -> addWidget(rightPanelSelectedNodesLabel , 6,0); propertiesGrid -> addWidget(rightPanelSelectedNodesLCD ,6,1); propertiesGrid -> addWidget(rightPanelSelectedEdgesLabel, 7,0); propertiesGrid -> addWidget(rightPanelSelectedEdgesLCD, 7,1); propertiesGrid -> addWidget(verticalSpaceLabel2, 8,0); propertiesGrid -> addWidget(rightPanelClickedNodeHeaderLabel, 9,0,1,2); propertiesGrid -> addWidget(rightPanelClickedNodeLabel , 10,0); propertiesGrid -> addWidget(rightPanelClickedNodeLCD ,10,1); propertiesGrid -> addWidget(rightPanelClickedNodeInDegreeLabel, 11,0); propertiesGrid -> addWidget(rightPanelClickedNodeInDegreeLCD,11,1); propertiesGrid -> addWidget(rightPanelClickedNodeOutDegreeLabel, 12,0); propertiesGrid -> addWidget(rightPanelClickedNodeOutDegreeLCD,12,1); propertiesGrid -> addWidget(rightPanelClickedNodeClucofLabel, 13,0); propertiesGrid -> addWidget(rightPanelClickedNodeClucofLCD,13,1); propertiesGrid -> addWidget(verticalSpaceLabel3, 15,0); propertiesGrid -> addWidget(rightPanelClickedEdgeHeaderLabel, 16,0,1,2); propertiesGrid -> addWidget(rightPanelClickedEdgeSourceLabel , 17,0); propertiesGrid -> addWidget(rightPanelClickedEdgeSourceLCD ,17,1); propertiesGrid -> addWidget(rightPanelClickedEdgeTargetLabel , 18,0); propertiesGrid -> addWidget(rightPanelClickedEdgeTargetLCD ,18,1); propertiesGrid -> addWidget(rightPanelClickedEdgeWeightLabel , 19,0); propertiesGrid -> addWidget(rightPanelClickedEdgeWeightLCD ,19,1); propertiesGrid -> setRowStretch(20,1); //make an invisible row stretch to rest of height //create a panel with title rightPanel = new QGroupBox(tr("Statistics Panel")); rightPanel->setMaximumWidth(210); rightPanel -> setLayout (propertiesGrid); } /** * @brief MainWindow::initView * Initializes the scene and the corresponding graphicsWidget, * The latter is a QGraphicsView canvas which is the main widget of SocNetV. */ void MainWindow::initView() { qDebug ()<< "MW::initView()"; //create a scene scene=new QGraphicsScene(); //create a view widget for this scene graphicsWidget=new GraphicsWidget(scene, this); graphicsWidget->setViewportUpdateMode( QGraphicsView::SmartViewportUpdate ); // FullViewportUpdate // MinimalViewportUpdate //SmartViewportUpdate //BoundingRectViewportUpdate //QGraphicsView can cache pre-rendered content in a QPixmap, which is then drawn onto the viewport. graphicsWidget->setCacheMode(QGraphicsView::CacheNone); //CacheBackground | CacheNone bool antialiasing = (appSettings["antialiasing"] == "true" ) ? true:false; graphicsWidget->setRenderHint(QPainter::Antialiasing, antialiasing ); graphicsWidget->setRenderHint( QPainter::TextAntialiasing, antialiasing ); graphicsWidget->setRenderHint(QPainter::SmoothPixmapTransform, antialiasing ); //Optimization flags: //if items do restore their state, it's not needed for graphicsWidget to do the same... graphicsWidget->setOptimizationFlag(QGraphicsView::DontSavePainterState, true); //Disables QGraphicsView's antialiasing auto-adjustment of exposed areas. graphicsWidget->setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing, false); //"QGraphicsScene applies an indexing algorithm to the scene, to speed up item discovery functions like items() and itemAt(). // Indexing is most efficient for static scenes (i.e., where items don't move around). // For dynamic scenes, or scenes with many animated items, the index bookkeeping can outweight the fast lookup speeds." So... scene->setItemIndexMethod(QGraphicsScene::BspTreeIndex); //NoIndex (for anime) | BspTreeIndex graphicsWidget->setTransformationAnchor(QGraphicsView::AnchorUnderMouse); //graphicsWidget->setTransformationAnchor(QGraphicsView::AnchorViewCenter); //graphicsWidget->setTransformationAnchor(QGraphicsView::NoAnchor); graphicsWidget->setResizeAnchor(QGraphicsView::AnchorViewCenter); // sets dragging the mouse over the scene while the left mouse button is pressed. graphicsWidget->setDragMode(QGraphicsView::RubberBandDrag); graphicsWidget->setFocusPolicy(Qt::StrongFocus); graphicsWidget->setFocus(); graphicsWidget->setWhatsThis(tr("The canvas of SocNetV. \n\n" "Inside this area you create and edit networks, " "load networks from files and visualize them \n" "according to selected metrics. \n\n" " - To create a new node, double-click anywhere (Ctrl+.)\n" " - To add an arc between two nodes, double-click" " on the first node then double-click on the second (Ctrl+/)\n" " - To change network appearance, right click on empty space\n" " - To change/edit the properties of a node, right-click on it\n" " - To change/edit the properties of an edge, right-click on it." "")); } /** * @brief MainWindow::initWindowLayout * Initializes the application window UI: * Creates helper widgets and sets the main layout of the MainWindow */ void MainWindow::initWindowLayout() { qDebug () << "MW::initWindowLayout"; int size = style()->pixelMetric(QStyle::PM_ToolBarIconSize); QSize iconSize(size, size); iconSize.setHeight(16); iconSize.setWidth(16); // Zoom slider zoomInBtn = new QToolButton; zoomInBtn->setShortcut(Qt::CTRL + Qt::Key_Plus); zoomInBtn->setToolTip(tr("Zoom in (Ctrl++)")); zoomInBtn->setStatusTip(tr("Zoom inside the actual network. Or press Cltr and use mouse wheel.")); zoomInBtn->setWhatsThis(tr("Zoom In.\n\n" "Zooms in the actual network" "You can also press Cltr and use mouse wheel.")); zoomInBtn->setAutoRepeat(true); zoomInBtn->setAutoRepeatInterval(33); zoomInBtn->setAutoRepeatDelay(0); zoomInBtn->setIcon(QPixmap(":/images/zoomin.png")); zoomInBtn->setIconSize(iconSize); zoomOutBtn = new QToolButton; zoomOutBtn->setAutoRepeat(true); zoomOutBtn->setShortcut(Qt::CTRL + Qt::Key_Minus); zoomOutBtn->setToolTip(tr("Zoom out (Ctrl+-)")); zoomOutBtn->setStatusTip(tr("Zoom out of the actual network. Or press Cltr and use mouse wheel.")); zoomOutBtn->setWhatsThis(tr("Zoom out.\n\n" "Zooms out the actual network" "You can also press Cltr and use mouse wheel.")); zoomOutBtn->setAutoRepeat(true); zoomOutBtn->setAutoRepeatInterval(33); zoomOutBtn->setAutoRepeatDelay(0); zoomOutBtn->setIcon(QPixmap(":/images/zoomout.png")); zoomOutBtn->setIconSize(iconSize); zoomSlider = new QSlider; zoomSlider->setMinimum(0); zoomSlider->setMaximum(500); zoomSlider->setValue(250); zoomSlider->setToolTip(tr("Zoom slider: Drag up to zoom in. \n" "Drag down to zoom out. ")); zoomSlider->setWhatsThis(tr("Zoom slider: Drag up to zoom in. \n" "Drag down to zoom out. ")); zoomSlider->setTickPosition(QSlider::TicksBothSides); // Zoom slider layout QVBoxLayout *zoomSliderLayout = new QVBoxLayout; zoomSliderLayout->addWidget(zoomInBtn); zoomSliderLayout->addWidget(zoomSlider); zoomSliderLayout->addWidget(zoomOutBtn); // Rotate slider rotateLeftBtn = new QToolButton; rotateLeftBtn->setAutoRepeat(true); rotateLeftBtn->setShortcut(Qt::CTRL + Qt::Key_Left); rotateLeftBtn->setIcon(QPixmap(":/images/rotateleft.png")); rotateLeftBtn->setToolTip(tr("Rotate counterclockwise (Ctrl+Left Arrow)")); rotateLeftBtn->setStatusTip(tr("Rotate counterclockwise (Ctrl+Left Arrow)")); rotateLeftBtn->setWhatsThis(tr("Rotates counterclockwise (Ctrl+Left Arrow)")); rotateLeftBtn->setIconSize(iconSize); rotateRightBtn = new QToolButton; rotateRightBtn->setAutoRepeat(true); rotateRightBtn->setShortcut(Qt::CTRL + Qt::Key_Right); rotateRightBtn ->setIcon(QPixmap(":/images/rotateright.png")); rotateRightBtn->setToolTip(tr("Rotate clockwise (Ctrl+Right Arrow)")); rotateRightBtn->setStatusTip(tr("Rotate clockwise (Ctrl+Right Arrow)")); rotateRightBtn->setWhatsThis(tr("Rotates clockwise (Ctrl+Right Arrow)")); rotateRightBtn ->setIconSize(iconSize); rotateSlider = new QSlider; rotateSlider->setOrientation(Qt::Horizontal); rotateSlider->setMinimum(-180); rotateSlider->setMaximum(180); rotateSlider->setTickInterval(5); rotateSlider->setValue(0); rotateSlider->setToolTip(tr("Rotate slider: Drag to left to rotate clockwise. \n" "Drag to right to rotate counterclockwise. ")); rotateSlider->setWhatsThis(tr("Rotate slider: Drag to left to rotate clockwise. " "Drag to right to rotate counterclockwise. ")); rotateSlider->setTickPosition(QSlider::TicksBothSides); // Rotate slider layout QHBoxLayout *rotateSliderLayout = new QHBoxLayout; rotateSliderLayout->addWidget(rotateLeftBtn); rotateSliderLayout->addWidget(rotateSlider); rotateSliderLayout->addWidget(rotateRightBtn ); resetSlidersBtn = new QToolButton; resetSlidersBtn->setText(tr("Reset")); resetSlidersBtn->setShortcut(Qt::CTRL + Qt::Key_0); resetSlidersBtn->setStatusTip(tr("Reset zoom and rotation to zero (or press Ctrl+0)")); resetSlidersBtn->setToolTip(tr("Reset zoom and rotation to zero (Ctrl+0)")); resetSlidersBtn->setWhatsThis(tr("Reset zoom and rotation to zero (Ctrl+0)")); resetSlidersBtn->setIcon(QPixmap(":/images/reset.png")); resetSlidersBtn ->setIconSize(iconSize); resetSlidersBtn->setEnabled(true); // Create a layout for the toolbox and the canvas. // This will be the layout of our MW central widget QGridLayout *layout = new QGridLayout; layout->addWidget(leftPanel, 0, 0, 2,1); layout->addWidget(graphicsWidget,0,1); layout->addLayout(zoomSliderLayout, 0, 2); layout->addWidget(rightPanel, 0, 3,2,1); layout->addLayout(rotateSliderLayout, 1, 1, 1, 1); layout->addWidget(resetSlidersBtn, 1, 2, 1, 1); //create a dummy widget, and set the above layout QWidget *widget = new QWidget; widget->setLayout(layout); //now set this as central widget of MW setCentralWidget(widget); // set panels visibility if ( appSettings["showRightPanel"] == "false") { slotOptionsRightPanelVisibility(false); } if ( appSettings["showLeftPanel"] == "false") { slotOptionsLeftPanelVisibility(false); } qDebug () << "MW::initWindowLayout - resize to 1280x900"; this->resize(1280,900); this->showMaximized(); } /** * @brief MainWindow::initSignalSlots * Connect signals & slots between various parts of the app: * - the GraphicsWidget and the Graph * - the GraphicsWidget and the MainWindow * This must be called after all widgets have been created. * */ void MainWindow::initSignalSlots() { qDebug ()<< "MW::initSignalSlots()"; // Signals between graphicsWidget and MainWindow connect( graphicsWidget, SIGNAL( resized(int, int)), &activeGraph, SLOT( canvasSizeSet(int,int)) ) ; connect( graphicsWidget, &GraphicsWidget::setCursor, this,&MainWindow::setCursor); connect( graphicsWidget, &GraphicsWidget::userClickOnEmptySpace, this, &MainWindow::slotEditClickOnEmptySpace ) ; connect( graphicsWidget, SIGNAL( userDoubleClickNewNode(const QPointF &) ), this, SLOT( slotEditNodeAddWithMouse(const QPointF &) ) ) ; connect( graphicsWidget, SIGNAL( userMiddleClicked(const int &, const int &) ), this, SLOT( slotEditEdgeCreate(const int &, const int &) ) ); connect( graphicsWidget, SIGNAL( openNodeMenu() ), this, SLOT( slotEditNodeOpenContextMenu() ) ) ; connect( graphicsWidget, SIGNAL( openEdgeMenu() ), this, SLOT( slotEditEdgeOpenContextMenu() ) ) ; connect (graphicsWidget, &GraphicsWidget::openContextMenu, this, &MainWindow::slotEditOpenContextMenu); connect( graphicsWidget, SIGNAL(userNodeMoved(const int &, const int &, const int &)), this, SLOT( slotEditNodePosition(const int &, const int &, const int &) ) ); connect( graphicsWidget, SIGNAL(zoomChanged(const int &)), zoomSlider, SLOT( setValue(const int &)) ); connect(zoomSlider, SIGNAL(valueChanged(const int &)), graphicsWidget, SLOT(changeMatrixScale(const int &))); connect( zoomInBtn, SIGNAL(clicked()), graphicsWidget, SLOT( zoomIn() ) ); connect( zoomOutBtn, SIGNAL(clicked()), graphicsWidget, SLOT( zoomOut() ) ); connect( graphicsWidget, SIGNAL(rotationChanged(const int &)), rotateSlider, SLOT( setValue(const int &)) ); connect(rotateSlider, SIGNAL(valueChanged(const int &)), graphicsWidget, SLOT(changeMatrixRotation(const int &))); connect(rotateLeftBtn, SIGNAL(clicked()), graphicsWidget, SLOT(rotateLeft())); connect(rotateRightBtn, SIGNAL(clicked()), graphicsWidget, SLOT(rotateRight())); connect(resetSlidersBtn, SIGNAL(clicked()), graphicsWidget, SLOT(reset())); // Signals between activeGraph and graphicsWidget connect( graphicsWidget, &GraphicsWidget::userSelectedItems, &activeGraph,&Graph::graphSelectionChanged); connect( &activeGraph, SIGNAL( addGuideCircle(const double&, const double&, const double&) ), graphicsWidget, SLOT( addGuideCircle(const double&, const double&, const double&) ) ) ; connect( &activeGraph, SIGNAL( addGuideHLine(const double&) ), graphicsWidget, SLOT( addGuideHLine(const double&) ) ) ; connect( &activeGraph, SIGNAL( setNodePos(const int &, const qreal &, const qreal &) ), graphicsWidget, SLOT( moveNode(const int &, const qreal &, const qreal &) ) ) ; connect( &activeGraph, SIGNAL( drawNode( const int &, const int &, const QString &, const QString &, const bool &,const bool &, const QString &, const int &, const int &, const bool &, const QString &, const QString &, const int &, const int &, const QPointF & ) ), graphicsWidget, SLOT( drawNode( const int &, const int &, const QString &, const QString &, const bool &,const bool &, const QString &, const int &, const int &, const bool &, const QString &, const QString &, const int &, const int &, const QPointF & ) ) ) ; connect( &activeGraph, SIGNAL( eraseEdge(const long int &, const long int &)), graphicsWidget, SLOT( eraseEdge(const long int &, const long int &) ) ); connect( &activeGraph, SIGNAL( drawEdge( const int&, const int&, const float &, const QString &, const QString &, const int&, const bool&, const bool&, const bool&)), graphicsWidget, SLOT( drawEdge( const int&, const int&, const float &, const QString &,const QString &, const int &, const bool&, const bool &, const bool&) ) ) ; connect( &activeGraph, SIGNAL( setEdgeWeight(const long int &, const long int &, const float &)), graphicsWidget, SLOT( setEdgeWeight(const long int &, const long int &, const float &) ) ); connect( &activeGraph, SIGNAL( setEdgeUndirected(const long int &, const long int &, const float &)), graphicsWidget, SLOT( setEdgeUndirected(const long int &, const long int &, const float &) ) ); connect( &activeGraph, SIGNAL( setEdgeColor(const long int &, const long int &, const QString &)), graphicsWidget, SLOT( setEdgeColor(const long int &, const long int &, const QString &) ) ); connect( &activeGraph, SIGNAL( setEdgeLabel(const long int &, const long int &, const QString &)), graphicsWidget, SLOT( setEdgeLabel(const long int &, const long int &, const QString &) ) ); connect( &activeGraph, SIGNAL( eraseNode(long int) ), graphicsWidget, SLOT( eraseNode(long int) ) ); connect( &activeGraph, SIGNAL( setEdgeVisibility (int, int, int, bool) ), graphicsWidget, SLOT( setEdgeVisibility (int, int, int, bool) ) ); connect( &activeGraph, SIGNAL( setVertexVisibility(long int, bool) ), graphicsWidget, SLOT( setNodeVisibility (long int , bool) ) ); connect( &activeGraph, SIGNAL( setNodeSize(const long int &, const int &) ), graphicsWidget, SLOT( setNodeSize (const long int &, const int &) ) ); connect( &activeGraph, SIGNAL( setNodeColor(long int,QString)) , graphicsWidget, SLOT( setNodeColor(long int, QString) ) ); connect( &activeGraph, SIGNAL( setNodeShape(long int,QString)) , graphicsWidget, SLOT( setNodeShape(long int, QString) ) ); connect( &activeGraph, SIGNAL( setNodeNumberSize(const long int &, const int &) ), graphicsWidget, SLOT( setNodeNumberSize (const long int &, const int &) ) ); connect( &activeGraph, SIGNAL( setNodeNumberDistance(const long int &, const int &) ), graphicsWidget, SLOT( setNodeNumberDistance (const long int &, const int &) ) ); connect( &activeGraph, &Graph::setNodeLabel , graphicsWidget, &GraphicsWidget::setNodeLabel ); connect( &activeGraph,&Graph::setNodeLabelColor, graphicsWidget, &GraphicsWidget::setNodeLabelColor ); connect( &activeGraph, SIGNAL( setNodeLabelSize(const long int &, const int &) ), graphicsWidget, SLOT( setNodeLabelSize (const long int &, const int &) ) ); connect( &activeGraph, SIGNAL( setNodeLabelDistance(const long int &, const int &) ), graphicsWidget, SLOT( setNodeLabelDistance (const long int &, const int &) ) ); connect( graphicsWidget, &GraphicsWidget::userClickedNode, &activeGraph, &Graph::vertexClickedSet ); connect( graphicsWidget, &GraphicsWidget::userClickedEdge, &activeGraph, &Graph::edgeClickedSet ); connect( &activeGraph, SIGNAL(signalRelationChangedToGW(int)), graphicsWidget, SLOT( relationSet(int)) ) ; //SIGNALS BETWEEN ACTIVEGRAPH AND MAINWINDOW connect( &activeGraph, &Graph::signalSelectionChanged, this, &MainWindow::slotEditSelectionChanged); connect( &activeGraph, &Graph::signalNodeClickedInfo , this, &MainWindow::slotEditNodeInfoStatusBar ); connect ( &activeGraph, &Graph::signalEdgeClickedInfo, this, &MainWindow::slotEditEdgeInfoStatusBar ); connect( &activeGraph, SIGNAL( signalGraphModified(const int &, const bool &, const int &, const int &, const float &) ), this, SLOT( slotNetworkChanged(const int &, const bool &, const int &, const int &, const float &) ) ) ; connect( &activeGraph, SIGNAL( signalGraphLoaded( const int &, const QString &, const QString &, const int &, const int &, const QString &) ), this, SLOT( slotNetworkFileLoaded( const int &, const QString &, const QString &, const int &, const int &, const QString &) ) ) ; connect( &activeGraph, SIGNAL( signalGraphSaved( const int &) ), this, SLOT( slotNetworkSaved( const int &) ) ) ; connect( &activeGraph, SIGNAL( statusMessage (QString) ), this, SLOT( statusMessage (QString) ) ) ; connect( &activeGraph, SIGNAL( signalDatasetDescription (QString) ), this, SLOT( slotHelpMessageToUserInfo (QString) ) ) ; connect( &activeGraph, &Graph::signalNodeSizesByInDegree, this, &MainWindow::slotLayoutNodeSizesByInDegree ); connect( editRelationChangeCombo , SIGNAL( activated(int) ) , &activeGraph, SLOT( relationSet(int) ) ); connect( editRelationChangeCombo , SIGNAL( currentTextChanged(QString) ), &activeGraph, SLOT( relationCurrentRename(QString) ) ); connect( this , &MainWindow::signalRelationAddAndChange, &activeGraph, &Graph::relationAdd ); connect( editRelationNextAct, &QAction::triggered, &activeGraph, &Graph::relationNext ); connect( editRelationPreviousAct, &QAction::triggered, &activeGraph, &Graph::relationPrev ); connect ( &activeGraph, &Graph::signalRelationChangedToMW, this, &MainWindow::slotEditRelationChange ); connect ( &activeGraph, &Graph::signalRelationsClear, this, &MainWindow::slotEditRelationsClear ); connect ( &activeGraph, &Graph::signalRelationAddToMW, this, &MainWindow::slotEditRelationAdd ); connect ( &activeGraph, &Graph::signalRelationRenamedToMW, this, &MainWindow::slotEditRelationRename ); //signals and slots inside MainWindow // connect( editNodeAddBt,SIGNAL(clicked()), this, SLOT( slotEditNodeAdd() ) ); // connect( editEdgeAddBt,SIGNAL(clicked()), this, SLOT( slotEditEdgeAdd() ) ); // connect( removeNodeBt,SIGNAL(clicked()), this, SLOT( slotEditNodeRemove() ) ); // connect( editEdgeRemoveBt,SIGNAL(clicked()), this, SLOT( slotEditEdgeRemove() ) ); connect( editRelationAddAct, SIGNAL(triggered()), this, SLOT(slotEditRelationAdd()) ); connect( editRelationRenameAct,SIGNAL(triggered()), this, SLOT(slotEditRelationRename()) ) ; connect( &m_DialogEdgeFilterByWeight, SIGNAL( userChoices( float, bool) ), &activeGraph, SLOT( edgeFilterByWeight (float, bool) ) ); connect( &m_WebCrawlerDialog, &WebCrawlerDialog::userChoices, this, &MainWindow::slotNetworkWebCrawler ); connect( &m_datasetSelectDialog, SIGNAL( userChoices( QString) ), this, SLOT( slotNetworkDataSetRecreate(QString) ) ); connect( layoutGuidesAct, SIGNAL(triggered(bool)), this, SLOT(slotLayoutGuides(bool))); connect(toolBoxEditNodeSubgraphSelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(toolBoxEditNodeSubgraphSelectChanged(int) ) ); connect(toolBoxEditEdgeModeSelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(slotEditEdgeMode(int) ) ); connect(toolBoxEditEdgeSymmetrizeSelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(toolBoxEditEdgeSymmetrizeSelectChanged(int) ) ); connect(toolBoxAnalysisMatricesSelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(toolBoxAnalysisMatricesSelectChanged(int) ) ); connect(toolBoxAnalysisCohesionSelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(toolBoxAnalysisCohesionSelectChanged(int) ) ); connect(toolBoxAnalysisStrEquivalenceSelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(toolBoxAnalysisStrEquivalenceSelectChanged(int) ) ); connect(toolBoxAnalysisCommunitiesSelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(toolBoxAnalysisCommunitiesSelectChanged(int) ) ); connect(toolBoxAnalysisProminenceSelect, SIGNAL (currentIndexChanged(int) ), this, SLOT(toolBoxAnalysisProminenceSelectChanged(int) ) ); connect(toolBoxNodeSizesByOutDegreeBx , SIGNAL(clicked(bool)), this, SLOT(slotLayoutNodeSizesByOutDegree(bool))); connect(toolBoxNodeSizesByInDegreeBx , SIGNAL(clicked(bool)), this, SLOT(slotLayoutNodeSizesByInDegree(bool))); connect(toolBoxLayoutByIndexButton, SIGNAL (clicked() ), this, SLOT(toolBoxLayoutByIndexButtonPressed() ) ); connect(toolBoxLayoutForceDirectedButton, SIGNAL (clicked() ), this, SLOT(toolBoxLayoutForceDirectedButtonPressed() ) ); connect( toolBoxLayoutGuidesBx, SIGNAL(clicked(bool)), this, SLOT(slotLayoutGuides(bool))); } /** * @brief MainWindow::initApp * Initializes the default network parameters. * Used on app start and especially when erasing a network to start a new one */ void MainWindow::initApp(){ qDebug()<<"MW::initApp() - START INITIALISATION"; QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); // Init basic variables considerWeights=false; inverseWeights=false; askedAboutWeights=false; previous_fileName=fileName; fileName=""; initFileCodec= "UTF-8"; networkSave->setIcon(QIcon(":/images/saved.png")); networkSave->setEnabled(true); markedNodesExist=false; //used by slotEditNodeFind() /** Clear LCDs **/ rightPanelClickedNodeInDegreeLCD->display(0); rightPanelClickedNodeOutDegreeLCD->display(0); rightPanelClickedNodeClucofLCD->display(0); rightPanelClickedNodeLCD->display(0); rightPanelClickedEdgeSourceLCD->display(0); rightPanelClickedEdgeTargetLCD->display(0); rightPanelClickedEdgeWeightLCD->display(0); /** Clear toolbox and menu checkboxes **/ toolBoxEditEdgeSymmetrizeSelect->setCurrentIndex(0); toolBoxEditEdgeModeSelect->setCurrentIndex(0); toolBoxAnalysisCommunitiesSelect->setCurrentIndex(0); toolBoxAnalysisStrEquivalenceSelect->setCurrentIndex(0); toolBoxAnalysisCohesionSelect->setCurrentIndex(0); toolBoxAnalysisProminenceSelect->setCurrentIndex(0); toolBoxLayoutByIndexSelect->setCurrentIndex(0); toolBoxLayoutByIndexTypeSelect ->setCurrentIndex(0); toolBoxLayoutForceDirectedSelect->setCurrentIndex(0); toolBoxNodeSizesByOutDegreeBx->setChecked(false); toolBoxNodeSizesByInDegreeBx->setChecked(false); optionsEdgeWeightNumbersAct->setChecked( (appSettings["initEdgeWeightNumbersVisibility"] == "true") ? true:false ); considerEdgeWeightsAct->setChecked(false); optionsEdgeArrowsAct->setChecked( (appSettings["initEdgeArrows"] == "true") ? true: false ); optionsEdgeLabelsAct->setChecked ( (appSettings["initEdgeLabelsVisibility"] == "true") ? true: false ); editFilterNodesIsolatesAct->setChecked(false); // re-init orphan nodes menu item editFilterEdgesUnilateralAct->setChecked(false); //editRelationChangeCombo->clear(); /** Clear previous network data */ activeGraph.clear(); activeGraph.setSocNetV_Version(VERSION); activeGraph.vertexShapeInit(appSettings["initNodeShape"]); activeGraph.vertexSizeInit(appSettings["initNodeSize"].toInt(0, 10)); activeGraph.vertexColorInit( appSettings["initNodeColor"] ); activeGraph.vertexNumberSizeInit(appSettings["initNodeNumberSize"].toInt(0,10)); activeGraph.vertexNumberColorInit(appSettings["initNodeNumberColor"]); activeGraph.vertexNumberDistanceInit(appSettings["initNodeNumberDistance"].toInt(0,10)); activeGraph.vertexLabelColorInit(appSettings["initNodeLabelColor"]); activeGraph.vertexLabelSizeInit(appSettings["initNodeLabelSize"].toInt(0,10)); activeGraph.vertexLabelDistanceInit(appSettings["initNodeLabelDistance"].toInt(0,10)); activeGraph.edgeColorInit(appSettings["initEdgeColor"]); activeGraph.vertexLabelsVisibilitySet( (appSettings["initNodeLabelsVisibility"] == "true" ) ? true: false ); activeGraph.vertexNumbersVisibilitySet( ( appSettings["initNodeNumbersVisibility"] == "true" ) ? true: false ); activeGraph.vertexNumbersInsideNodesSet( ( appSettings["initNodeNumbersInside"] == "true" ) ? true: false ); /** Clear graphicsWidget scene and reset transformations **/ graphicsWidget->clear(); rotateSlider->setValue(0); zoomSlider->setValue(250); graphicsWidget->setInitZoomIndex(250); graphicsWidget->setInitNodeSize(appSettings["initNodeSize"].toInt(0, 10)); if (appSettings["initBackgroundImage"] != "" && QFileInfo(appSettings["initBackgroundImage"]).exists()) { graphicsWidget->setBackgroundBrush(QImage(appSettings["initBackgroundImage"])); graphicsWidget->setCacheMode(QGraphicsView::CacheBackground); statusMessage( tr("BackgroundImage on.") ); } else { graphicsWidget->setBackgroundBrush( QBrush(QColor (appSettings["initBackgroundColor"])) ); } qDebug()<<"MW::initApp() - Clearing my" <close(); delete ed; } m_textEditors.clear(); /** set window title **/ setWindowTitle(tr("Social Network Visualizer ")+VERSION); QApplication::restoreOverrideCursor(); setCursor(Qt::ArrowCursor); statusMessage( tr("Ready")); qDebug()<< "MW::initApp() - INITIALISATION END"; } void MainWindow::slotNetworkFileRecentUpdateActions() { int numRecentFiles = qMin(recentFiles.size(), (int)MaxRecentFiles); for (int i = 0; i < numRecentFiles; ++i) { QString text = tr("&%1 %2").arg(i + 1).arg(QFileInfo(recentFiles[i]).fileName()); recentFileActs[i]->setText(text); recentFileActs[i]->setData(recentFiles[i]); recentFileActs[i]->setVisible(true); } for (int j = numRecentFiles; j < MaxRecentFiles; ++j) recentFileActs[j]->setVisible(false); //separatorAct->setVisible(numRecentFiles > 0); } /** * @brief MainWindow::statusMessage * @param message * Convenience method to show a message in the status bar, with the given duration * Slot called by Graph::statusMessage to display some message to the user */ void MainWindow::statusMessage(const QString message){ statusBar()->showMessage( message, appSettings["initStatusBarDuration"].toInt(0)); } /** * @brief MainWindow::slotHelpMessageToUserInfo * @param text * Helper function to display a useful info message */ void MainWindow::slotHelpMessageToUserInfo(const QString text) { slotHelpMessageToUser(USER_MSG_INFO,tr("Useful information"), text ); } /** * @brief MainWindow::slotHelpMessageToUserError * @param text * Helper function to display a useful error message */ void MainWindow::slotHelpMessageToUserError(const QString text) { slotHelpMessageToUser(USER_MSG_CRITICAL ,tr("Error"), text ); } /** * @brief MainWindow::slotHelpMessageToUser * Convenience method * @param message */ int MainWindow::slotHelpMessageToUser(const int type, const QString statusMsg, const QString text, const QString info, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defBtn, const QString btn1, const QString btn2 ) { int response=0; QMessageBox msgBox; QPushButton *pbtn1, *pbtn2; switch (type) { case USER_MSG_INFO: if (!statusMsg.isNull()) statusMessage( statusMsg ); msgBox.setText(text); if (!info.isNull()) msgBox.setInformativeText(info); msgBox.setIcon(QMessageBox::Information); if (buttons==QMessageBox::NoButton) { msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); } else { msgBox.setStandardButtons(buttons); msgBox.setDefaultButton(defBtn); } msgBox.setDefaultButton(defBtn); response = msgBox.exec(); break; case USER_MSG_CRITICAL: if (!statusMsg.isNull()) statusMessage( statusMsg ); msgBox.setText(text); if (!info.isNull()) msgBox.setInformativeText(info); //msgBox.setTextFormat(Qt::RichText); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); response = msgBox.exec(); break; case USER_MSG_CRITICAL_NO_NETWORK: statusMessage( tr("Nothing to do! Load or create a social network first") ); msgBox.setText( tr("No network! \n" "Load social network data or create a new social network first. \n") ); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); response = msgBox.exec(); break; case USER_MSG_CRITICAL_NO_EDGES: statusMessage( tr("Nothing to do! Load social network data or create edges first") ); msgBox.setText( tr("No edges! \n" "Load social network data or create some edges first. \n") ); msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); response = msgBox.exec(); break; case USER_MSG_QUESTION: if (!statusMsg.isNull()) statusMessage( statusMsg ); msgBox.setText( text ); if (!info.isNull()) msgBox.setInformativeText(info); if (buttons==QMessageBox::NoButton) { msgBox.setStandardButtons(QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Yes); } else { msgBox.setStandardButtons(buttons); msgBox.setDefaultButton(defBtn); } msgBox.setIcon(QMessageBox::Question); response = msgBox.exec(); break; case USER_MSG_QUESTION_CUSTOM: // a custom question with just two buttons if (!statusMsg.isNull()) statusMessage( statusMsg ); msgBox.setText( text ); if (!info.isNull()) msgBox.setInformativeText(info); pbtn1 = msgBox.addButton(btn1, QMessageBox::ActionRole); pbtn2 = msgBox.addButton(btn2, QMessageBox::ActionRole); msgBox.setIcon(QMessageBox::Question); response = msgBox.exec(); if (msgBox.clickedButton() == pbtn1 ) { response=1; } else if (msgBox.clickedButton() == pbtn2 ) { response=2; } break; default: //just for sanity if (!statusMsg.isNull()) statusMessage( statusMsg ); msgBox.setText( text ); msgBox.setIcon(QMessageBox::Information); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setDefaultButton(QMessageBox::Ok); response = msgBox.exec(); break; } return response; } /** * @brief Called from MW, when user selects something in the Subgraph from Selected * Nodes selectbox of the toolbox * @param selectedIndex */ void MainWindow::toolBoxEditNodeSubgraphSelectChanged(int selectedIndex) { qDebug()<< "MW::toolBoxEditNodeSubgraphSelectChanged " "selected text index: " << selectedIndex; switch(selectedIndex){ case 0: break; case 1: slotEditNodeSelectedToClique(); break; case 2: slotEditNodeSelectedToStar(); break; case 3: slotEditNodeSelectedToCycle(); break; case 4: slotEditNodeSelectedToLine(); break; }; } /** * @brief Called from MW, when user selects something in the Edge Symmetrize * selectbox of the toolbox * @param selectedIndex */ void MainWindow::toolBoxEditEdgeSymmetrizeSelectChanged(int selectedIndex) { qDebug()<< "MW::toolBoxEditEdgeSymmetrizeSelectChanged " "selected text index: " << selectedIndex; switch(selectedIndex){ case 0: break; case 1: slotEditEdgeSymmetrizeAll(); break; case 2: slotEditEdgeSymmetrizeStrongTies(); break; case 3: slotEditEdgeSymmetrizeCocitation(); break; }; } /** * @brief Called from MW, when user selects something in the Matrices * selectbox of the toolbox * @param selectedIndex */ void MainWindow::toolBoxAnalysisMatricesSelectChanged(int selectedIndex) { qDebug()<< "MW::toolBoxAnalysisMatricesSelectChanged " "selected text index: " << selectedIndex; switch(selectedIndex){ case 0: break; case 1: slotNetworkViewSociomatrix(); break; case 2: slotNetworkViewSociomatrixPlotText(); break; case 3: slotAnalyzeMatrixAdjacencyInverse(); break; case 4: slotAnalyzeMatrixAdjacencyTranspose(); break; case 5: slotAnalyzeMatrixAdjacencyCocitation(); break; case 6: slotAnalyzeMatrixDegree(); break; case 7: slotAnalyzeMatrixLaplacian(); break; }; } /** * @brief Called from MW, when user selects something in the Cohesion * selectbox of the toolbox to compute basic graph theoretic / network properties * @param selectedIndex */ void MainWindow::toolBoxAnalysisCohesionSelectChanged(int selectedIndex) { qDebug()<< "MW::toolBoxAnalysisCohesionSelectChanged " "selected text index: " << selectedIndex; switch(selectedIndex){ case 0: break; case 1: slotAnalyzeSymmetryCheck(); break; case 2: slotAnalyzeDistance(); break; case 3: slotAnalyzeDistanceAverage(); break; case 4: slotAnalyzeMatrixDistances(); break; case 5: slotAnalyzeMatrixGeodesics(); break; case 6: slotAnalyzeEccentricity(); break; case 7: slotAnalyzeDiameter(); break; case 8: slotAnalyzeConnectedness(); break; case 9: slotAnalyzeWalksLength(); break; case 10: slotAnalyzeWalksTotal(); break; case 11: slotAnalyzeReachabilityMatrix(); break; case 12: slotAnalyzeClusteringCoefficient(); break; }; } /** * @brief Called from MW, when user selects something in the Communities selectbox * of the toolbox * @param selectedIndex * */ void MainWindow::toolBoxAnalysisCommunitiesSelectChanged(int selectedIndex) { qDebug()<< "MW::toolBoxAnalysisCommunitiesSelectChanged " "selected text index: " << selectedIndex; switch(selectedIndex){ case 0: break; case 1: qDebug()<< "Cliques"; slotAnalyzeCommunitiesCliqueCensus(); break; case 2: qDebug() << "Triad Census"; slotAnalyzeCommunitiesTriadCensus(); break; }; } /** * @brief Called from MW, when user selects something in the Structural Equivalence * selectbox of the toolbox * @param selectedIndex * */ void MainWindow::toolBoxAnalysisStrEquivalenceSelectChanged(int selectedIndex) { qDebug()<< "MW::toolBoxAnalysisStrEquivalenceSelectChanged " "selected text index: " << selectedIndex; switch(selectedIndex){ case 0: break; case 1: qDebug()<< "Pearson"; slotAnalyzeStrEquivalencePearsonDialog(); break; case 2: qDebug()<< "Similarities"; slotAnalyzeStrEquivalenceSimilarityMeasureDialog(); break; case 3: qDebug() << "Dissimilarities"; slotAnalyzeStrEquivalenceDissimilaritiesDialog(); break; case 4: qDebug() << "Hierarchical Clustering"; slotAnalyzeStrEquivalenceClusteringHierarchicalDialog(); break; }; } /** * @brief Called from MW, when user selects something in the Prominence selectbox * of the toolbox * @param selectedIndex * */ void MainWindow::toolBoxAnalysisProminenceSelectChanged(int selectedIndex) { qDebug()<< "MW::toolBoxAnalysisProminenceSelectChanged " "selected text index: " << selectedIndex; switch(selectedIndex){ case 0: break; case 1: slotAnalyzeCentralityDegree(); break; case 2: slotAnalyzeCentralityCloseness(); break; case 3: slotAnalyzeCentralityClosenessIR(); break; case 4: slotAnalyzeCentralityBetweenness(); break; case 5: slotAnalyzeCentralityStress(); break; case 6: slotAnalyzeCentralityEccentricity(); break; case 7: slotAnalyzeCentralityPower(); break; case 8: slotAnalyzeCentralityInformation(); break; case 9: slotAnalyzeCentralityEigenvector(); break; case 10: slotAnalyzePrestigeDegree(); break; case 11: slotAnalyzePrestigePageRank(); break; case 12: slotAnalyzePrestigeProximity(); break; }; } /** * @brief MainWindow::toolBoxLayoutByIndexButtonPressed * Called from MW, when user selects a Prominence index in the Layout selectbox * of the left panel. */ void MainWindow::toolBoxLayoutByIndexButtonPressed(){ qDebug()<<"MW::toolBoxLayoutByIndexButtonPressed()"; int selectedIndex = toolBoxLayoutByIndexSelect->currentIndex(); QString selectedIndexText = toolBoxLayoutByIndexSelect -> currentText(); int selectedLayoutType = toolBoxLayoutByIndexTypeSelect ->currentIndex(); qDebug() << " selected index is " << selectedIndexText << " : " << selectedIndex << " selected layout type is " << selectedLayoutType; switch(selectedIndex) { case 0: break; case 1: if (selectedLayoutType==0) slotLayoutCircularRandom(); else if (selectedLayoutType==1) slotLayoutRandom(); break; default: if (selectedLayoutType==0) slotLayoutCircularByProminenceIndex(selectedIndexText); else if (selectedLayoutType==1) slotLayoutLevelByProminenceIndex(selectedIndexText); else if (selectedLayoutType==2){ slotLayoutNodeSizesByProminenceIndex(selectedIndexText); // re-init other options for node sizes... nodeSizesByOutDegreeAct->setChecked(false); toolBoxNodeSizesByOutDegreeBx->setChecked(false); nodeSizesByInDegreeAct->setChecked(false); toolBoxNodeSizesByInDegreeBx->setChecked(false); } break; }; } /** * @brief MainWindow::toolBoxLayoutForceDirectedButtonPressed * Called from MW, when user selects a model in the Layout by Force Directed * selectbox of left panel. */ void MainWindow::toolBoxLayoutForceDirectedButtonPressed(){ qDebug()<<"MW::toolBoxLayoutForceDirectedButtonPressed()"; int selectedModel = toolBoxLayoutForceDirectedSelect->currentIndex(); QString selectedModelText = toolBoxLayoutForceDirectedSelect -> currentText(); qDebug() << " selected index is " << selectedModelText << " : " << selectedModel; switch(selectedModel) { case 0: break; case 1: slotLayoutGuides(false); slotLayoutSpringEmbedder(); break; case 2: slotLayoutGuides(false); slotLayoutFruchterman(); break; case 3: slotLayoutGuides(false); slotLayoutKamadaKawai(); break; default: toolBoxLayoutForceDirectedSelect->setCurrentIndex(0); break; }; } /** * @brief MainWindow::resizeEvent * Resizes the scene when the window is resized. */ void MainWindow::resizeEvent( QResizeEvent * ){ qDebug() << "MW::resizeEvent(): Window resized to" << width() << "," << height() <<"Calling activeGraph.canvasSizeSet() to set canvas width and height"; activeGraph.canvasSizeSet(graphicsWidget->width(),graphicsWidget->height()); statusMessage( QString( tr("Window resized to (%1, %2)px. Canvas size: (%3, %4) px")) .arg(width()).arg(height()) .arg(graphicsWidget->width()).arg(graphicsWidget->height()) ); } /** * @brief MainWindow::closeEvent * @param ce * Closes the application. Asks to write any unsaved network data. */ void MainWindow::closeEvent( QCloseEvent* ce ) { qDebug() << "MW::closeEvent()"; if ( activeGraph.graphSaved() ) { ce->accept(); return; } switch( slotHelpMessageToUser( USER_MSG_QUESTION, tr("Save changes"), tr("Modified network has not been saved!"), tr("Do you want to save the changes to the network file?"), QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, QMessageBox::Cancel ) ) { case QMessageBox::Yes: slotNetworkSave(); ce->accept(); break; case QMessageBox::No: ce->accept(); break; case QMessageBox::NoButton: default: // just for sanity ce->ignore(); break; } initApp(); } /** * @brief MainWindow::slotNetworkNew * Creates a new network */ void MainWindow::slotNetworkNew() { slotNetworkClose(); } /** * @brief MainWindow::getLastPath * returns the last path used by user to open/save something */ QString MainWindow::getLastPath() { if ( appSettings["lastUsedDirPath"] == "socnetv-initial-none") { appSettings["lastUsedDirPath"] = appSettings["dataDir"]; } qDebug()<< "MW::getLastPath()" << appSettings["lastUsedDirPath"] ; return appSettings["lastUsedDirPath"] ; } /** * @brief MainWindow::setLastPath * sets the last path used by user to open/save something * @param filePath */ void MainWindow::setLastPath(QString filePath) { qDebug()<< "MW::setLastPath() for " << filePath; appSettings["lastUsedDirPath"] = QFileInfo(filePath).dir().absolutePath(); if ( !QFileInfo(filePath).completeSuffix().toLower().contains( "bmp" ) && !QFileInfo(filePath).completeSuffix().toLower().contains( "jpg" ) && !QFileInfo(filePath).completeSuffix().toLower().contains( "png" ) && !QFileInfo(filePath).completeSuffix().toLower().contains( "pdf" ) ) { recentFiles.removeAll(filePath); recentFiles.prepend(filePath); while(recentFiles.size() > MaxRecentFiles ) recentFiles.removeLast(); } slotNetworkFileRecentUpdateActions(); saveSettings(); qDebug() << appSettings["lastUsedDirPath"]; } /** * @brief MainWindow::slotNetworkFileChoose * If m_fileName is empty, opens a file selection dialog * Then calls slotNetworkFilePreview() * Called on application loading from command line with filename parameter * Called from slotNetworkImport* methods * Called from slotNetworkFileLoadRecent * @param m_fileName * @param m_fileFormat * @param checkSelectFileType */ void MainWindow::slotNetworkFileChoose(QString m_fileName, int m_fileFormat, const bool &checkSelectFileType) { qDebug() << "MW::slotNetworkFileChoose() - " << " m_fileName: " << m_fileName << " m_fileFormat " << m_fileFormat << " checkSelectFileType " << checkSelectFileType; previous_fileName=fileName; QString fileType_filter; /* * CASE 1: No filename provided. This happens when: * - User clicked Open Network File or * - User clicked Import Network * * Prepare known filetypes and * Open a file selection dialog for the user * */ if (m_fileName.isNull() || m_fileName.isEmpty() ) { fileType=m_fileFormat; // prepare supported filetype extensions switch (fileType){ case FILE_GRAPHML: fileType_filter = tr("GraphML (*.graphml *.xml);;All (*)"); break; case FILE_PAJEK: fileType_filter = tr("Pajek (*.net *.paj *.pajek);;All (*)"); break; case FILE_ADJACENCY: fileType_filter = tr("Adjacency (*.csv *.sm *.adj *.txt);;All (*)"); break; case FILE_GRAPHVIZ: fileType_filter = tr("GraphViz (*.dot);;All (*)"); break; case FILE_UCINET: fileType_filter = tr("UCINET (*.dl *.dat);;All (*)"); break; case FILE_GML: fileType_filter = tr("GML (*.gml);;All (*)"); break; case FILE_EDGELIST_WEIGHTED: fileType_filter = tr("Weighted Edge List (*.csv *.txt *.list *.edgelist *.lst *.wlst);;All (*)"); break; case FILE_EDGELIST_SIMPLE: fileType_filter = tr("Simple Edge List (*.csv *.txt *.list *.edgelist *.lst);;All (*)"); break; case FILE_TWOMODE: fileType_filter = tr("Two-Mode Sociomatrix (*.2sm *.aff);;All (*)"); break; default: //All fileType_filter = tr("GraphML (*.graphml *.xml);;" "GML (*.gml *.xml);;" "Pajek (*.net *.pajek *.paj);;" "UCINET (*.dl *.dat);;" "Adjacency (*.csv *.adj *.sm *.txt);;" "GraphViz (*.dot);;" "Weighted Edge List (*.csv *.txt *.edgelist *.list *.lst *.wlst);;" "Simple Edge List (*.csv *.txt *.edgelist *.list *.lst);;" "Two-Mode Sociomatrix (*.2sm *.aff);;" "All (*)"); break; } //prepare the filedialog QFileDialog *fileDialog = new QFileDialog(this); fileDialog->setFileMode(QFileDialog::ExistingFile); fileDialog->setNameFilter(fileType_filter); fileDialog->setViewMode(QFileDialog::Detail); fileDialog->setDirectory(getLastPath()); //connect its signals to our slots connect ( fileDialog, &QFileDialog::filterSelected, this, &MainWindow::slotNetworkFileDialogFilterSelected); connect ( fileDialog, &QFileDialog::fileSelected, this, &MainWindow::slotNetworkFileDialogFileSelected); connect ( fileDialog, &QFileDialog::rejected , this, &MainWindow::slotNetworkFileDialogRejected); //open the filedialog statusMessage( tr("Choose a network file...")); if (fileDialog->exec()) { m_fileName = (fileDialog->selectedFiles()).at(0); qDebug() << "MW::slotNetworkFileChoose() - m_fileName " << m_fileName; } else { //display some error statusMessage( tr("Nothing to do...")); } return; } /* * CASE 2: Filename provided. This happens when: * - Application starts from command line with filename parameter or * - User selects a Recent File or * - User selects a file in a previous slotNetworkFileChoose call * * If checkSelectFileType==TRUE (that is on app start or Recent File), * it tries to understand fileType by file extension. If file has unknown * file extension or an ambiguous file extension used by many different file * formats, then it asks the user to provide the fileType. Then it loads the * file * * If checkSelectFileType==FALSE, then it loads the file with given fileType. * */ if (checkSelectFileType || m_fileFormat==FILE_UNRECOGNIZED) { // This happens only on application startup or on loading a recent file. if ( ! m_fileName.endsWith(".graphml",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".net",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".paj",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".pajek",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".dl",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".gml",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".wlst",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".wlist",Qt::CaseInsensitive )&& ! m_fileName.endsWith(".2sm",Qt::CaseInsensitive ) && ! m_fileName.endsWith(".aff",Qt::CaseInsensitive )) { //ambigious tempFileNameNoPath=m_fileName.split ("/"); QStringList fileTypes; fileTypes << tr("GraphML") << tr("GML") << tr("Pajek") << tr("UCINET") << tr("Adjacency") << tr("GraphViz") << tr("Edge List (weighted)") << tr("Edge List (simple, non-weighted)") << tr("Two-mode sociomatrix") ; bool ok; QString userFileType= QInputDialog::getItem( this, tr("Selected file has ambiguous file extension!"), tr("You selected: %1 \n" "The name of this file has either an unknown extension \n" "or an extension used by different network file formats.\n\n" "SocNetV supports the following social network file " "formats. \nIn parentheses the expected extension. \n" "- GraphML (.graphml or .xml)\n" "- GML (.gml or .xml)\n" "- Pajek (.paj or .pajek or .net)\n" "- UCINET (.dl .dat) \n" "- GraphViz (.dot)\n" "- Adjacency Matrix (.sm or .adj or .csv or .txt)\n" "- Simple Edge List (.list or .lst)\n" "- Weighted Edge List (.wlist or .wlst)\n" "- Two-Mode / affiliation (.2sm or .aff) \n\n" "If you are sure the file is of a supported format, please \n" "select the right format from the list below."). arg(tempFileNameNoPath.last()), fileTypes, 0, false, &ok); if (ok && !userFileType.isEmpty()) { if (userFileType == "GraphML") { m_fileFormat=FILE_GRAPHML; } else if (userFileType == "GraphML") { m_fileFormat=FILE_PAJEK; } else if (userFileType == "GML") { m_fileFormat=FILE_GML; } else if (userFileType == "UCINET") { m_fileFormat=FILE_UCINET; } else if (userFileType == "Adjacency") { m_fileFormat=FILE_ADJACENCY; } else if (userFileType == "GraphViz") { m_fileFormat=FILE_GRAPHVIZ; } else if (userFileType == "Edge List (weighted)") { m_fileFormat=FILE_EDGELIST_WEIGHTED; } else if (userFileType == "Edge List (simple, non-weighted)") { m_fileFormat=FILE_EDGELIST_SIMPLE; } else if (userFileType == "Two-mode sociomatrix") { m_fileFormat=FILE_TWOMODE; } } else { statusMessage( tr("Opening network file aborted.")); //if a file was previously opened, get back to it. if (activeGraph.graphLoaded()) { fileName=previous_fileName; } return; } } else if (m_fileName.endsWith(".graphml",Qt::CaseInsensitive ) || m_fileName.endsWith(".xml",Qt::CaseInsensitive ) ) { m_fileFormat=FILE_GRAPHML; } else if (m_fileName.endsWith(".net",Qt::CaseInsensitive ) || m_fileName.endsWith(".paj",Qt::CaseInsensitive ) || m_fileName.endsWith(".pajek",Qt::CaseInsensitive ) ) { m_fileFormat=FILE_PAJEK; } else if (m_fileName.endsWith(".dl",Qt::CaseInsensitive ) || m_fileName.endsWith(".dat",Qt::CaseInsensitive ) ) { m_fileFormat=FILE_UCINET; } else if (m_fileName.endsWith(".sm",Qt::CaseInsensitive ) || m_fileName.endsWith(".csv",Qt::CaseInsensitive ) || m_fileName.endsWith(".adj",Qt::CaseInsensitive ) || m_fileName.endsWith(".txt",Qt::CaseInsensitive )) { m_fileFormat=FILE_ADJACENCY; } else if (m_fileName.endsWith(".dot",Qt::CaseInsensitive ) ) { m_fileFormat=FILE_GRAPHVIZ; } else if (m_fileName.endsWith(".gml",Qt::CaseInsensitive ) ) { m_fileFormat=FILE_GML; } else if (m_fileName.endsWith(".list",Qt::CaseInsensitive ) || m_fileName.endsWith(".lst",Qt::CaseInsensitive ) ) { m_fileFormat=FILE_EDGELIST_SIMPLE; } else if (m_fileName.endsWith(".wlist",Qt::CaseInsensitive ) || m_fileName.endsWith(".wlst",Qt::CaseInsensitive ) ) { m_fileFormat=FILE_EDGELIST_WEIGHTED; } else if (m_fileName.endsWith(".2sm",Qt::CaseInsensitive ) || m_fileName.endsWith(".aff",Qt::CaseInsensitive ) ) { m_fileFormat=FILE_TWOMODE; } else m_fileFormat=FILE_UNRECOGNIZED; } qDebug()<<"MW::slotNetworkFileChoose() - Calling slotNetworkFilePreview" << "with m_fileName" << m_fileName << "and m_fileFormat " << m_fileFormat; slotNetworkFilePreview(m_fileName, m_fileFormat ); } void MainWindow::slotNetworkFileDialogRejected() { qDebug() << "MW::slotNetworkFileDialogRejected() - if a file was previously opened, get back to it."; statusMessage( tr("Opening aborted")); } /** * @brief MainWindow::slotNetworkFileDialogFilterSelected * @param filter * Called when user selects a file filter (i.e. GraphML) in the fileDialog */ void MainWindow::slotNetworkFileDialogFilterSelected(const QString &filter) { qDebug() << "MW::slotNetworkFileDialogFilterSelected() - filter" << filter; if (filter.startsWith("GraphML",Qt::CaseInsensitive ) ) { fileType=FILE_GRAPHML; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FILE_GRAPHML"; } else if (filter.contains("PAJEK",Qt::CaseInsensitive ) ) { fileType=FILE_PAJEK; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FILE_PAJEK"; } else if (filter.contains("DL",Qt::CaseInsensitive ) || filter.contains("UCINET",Qt::CaseInsensitive ) ) { fileType=FILE_UCINET; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FILE_UCINET"; } else if (filter.contains("Adjancency",Qt::CaseInsensitive ) ) { fileType=FILE_ADJACENCY; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FILE_ADJACENCY"; } else if (filter.contains("GraphViz",Qt::CaseInsensitive ) ) { fileType=FILE_GRAPHVIZ; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FILE_GRAPHVIZ"; } else if (filter.contains("GML",Qt::CaseInsensitive ) ) { fileType=FILE_GML; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FILE_GML"; } else if (filter.contains("Simple Edge List",Qt::CaseInsensitive ) ) { fileType=FILE_EDGELIST_SIMPLE; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FILE_EDGELIST_SIMPLE"; } else if (filter.contains("Weighted Edge List",Qt::CaseInsensitive ) ) { fileType=FILE_EDGELIST_WEIGHTED; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FILE_EDGELIST_WEIGHTED"; } else if (filter.contains("Two-Mode",Qt::CaseInsensitive ) ) { fileType=FILE_TWOMODE; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FILE_TWOMODE"; } else { fileType=FILE_UNRECOGNIZED; qDebug() << "MW::slotNetworkFileDialogFilterSelected() - fileType FILE_UNRECOGNIZED"; } } /** * @brief MainWindow::slotNetworkFileDialogFileSelected * @param fileName * Called when user selects a file in the fileDialog * Calls slotNetworkFileChoose() again. */ void MainWindow::slotNetworkFileDialogFileSelected(const QString &fileName) { qDebug() << "MW::slotNetworkFileDialogFileSelected() - filename " << fileName << "calling slotNetworkFileChoose() with fileType" << fileType; slotNetworkFileChoose( fileName, fileType, ( (fileType==FILE_UNRECOGNIZED) ? true : false ) ); } /** * @brief MainWindow::slotNetworkSave * Saves the network in the same file. * First check if a fileName is currently used * If not, calls slotNetworkSaveAs (which prompts for a fileName before returning here) * If a fileName is currently set, it checks if fileFormat is supported for export * If not supported, and the file is new, just tries to save in GraphML * For other exporing options the user is informed to use the export menu. */ void MainWindow::slotNetworkSave(const int &fileFormat) { statusMessage( tr("Saving file...")); if (activeGraph.vertices() == 0) { statusMessage( QString(tr("Nothing to save. There are no vertices.")) ); } if (activeGraph.graphSaved()) { statusMessage( QString(tr("Graph already saved.")) ); } if ( fileName.isEmpty() ) { slotNetworkSaveAs(); return; } QFileInfo fileInfo (fileName); fileNameNoPath = fileInfo.fileName(); if ( activeGraph.graphFileFormatExportSupported( fileFormat ) ) { activeGraph.graphSave(fileName, fileFormat ) ; } else if (activeGraph.graphFileFormat()==FILE_GRAPHML || ( activeGraph.graphSaved() && !activeGraph.graphLoaded() ) ) { //new file or GraphML activeGraph.graphSave(fileName, FILE_GRAPHML); } else if ( activeGraph.graphFileFormatExportSupported( activeGraph.graphFileFormat() ) ) { activeGraph.graphSave(fileName, activeGraph.graphFileFormat() ) ; } else { switch( slotHelpMessageToUser (USER_MSG_QUESTION, tr("Save to GraphML?"), tr("Default File Format: GraphML "), tr("This network will be saved in GraphML format " "which is the default file format of SocNetV. \n\n" "Is this OK? \n\n" "If not, press Cancel, then go to Network > Export menu " "to see other supported formats to export your data to.") ) ) { case QMessageBox::Yes: activeGraph.graphSave(fileName, FILE_GRAPHML); break; case QMessageBox::Cancel: case QMessageBox::No: statusMessage( tr("Save aborted...") ); break; } } } /** * @brief MainWindow::slotNetworkSaveAs * Saves the network in a new file */ void MainWindow::slotNetworkSaveAs() { qDebug() << "MW::slotNetworkSaveAs()"; statusMessage( tr("Enter or select a filename to save the network...")); QString fn = QFileDialog::getSaveFileName( this, tr("Save Network to GraphML File Named..."), getLastPath(), tr("GraphML (*.graphml *.xml);;All (*)") ); if (!fn.isEmpty()) { if ( QFileInfo(fn).suffix().isEmpty() ){ slotHelpMessageToUser ( USER_MSG_INFO, tr("Appending .graphml"), tr("Missing Extension. \n" "Appending a standard .graphml to the given filename.") ); fn.append(".graphml"); } /** \todo Change the suffix automatically to graphML even if the user * has selected other? */ if ( !QFileInfo(fn).suffix().contains("graphML") || !QFileInfo(fn).suffix().contains("xml") ) { //fn = QFileInfo(fn).absoluteDir() + QFileInfo(fn).baseName() } fileName=fn; QFileInfo fileInfo (fileName); fileNameNoPath = fileInfo.fileName(); setLastPath(fileName); // store this path slotNetworkSave(FILE_GRAPHML); } else { statusMessage( tr("Saving aborted")); return; } } /** * @brief MainWindow::slotNetworkSaved * @param saved_ok * Called from Graph when we save file. * Updates Save icon and window title. */ void MainWindow::slotNetworkSaved(const int &status) { if (status <= 0) { statusMessage( tr("Error! Could not save this file: %1").arg (fileNameNoPath)); } else { networkSave->setIcon(QIcon(":/images/saved.png")); networkSave->setEnabled(false); setWindowTitle( fileNameNoPath ); statusMessage( tr("Network saved under filename: %1").arg (fileNameNoPath)); } } /** * @brief MainWindow::slotNetworkClose * Closes the network. Saves it if necessary. Used by createNew. */ void MainWindow::slotNetworkClose() { qDebug()<<"slotNetworkClose()"; statusMessage( tr("Closing network file...")); if (!activeGraph.graphSaved()) { switch ( slotHelpMessageToUser ( USER_MSG_QUESTION, tr("Closing Network..."), tr("Network has not been saved. \n" "Do you want to save before closing it?") ) ) { case QMessageBox::Yes: slotNetworkSave(); break; case QMessageBox::No: break; case QMessageBox::Cancel: return; break; } } statusMessage( tr("Erasing old network data....")); initApp(); statusMessage( tr("Ready.")); } /** * @brief MainWindow::slotNetworkPrint * Sends the active network to the printer */ void MainWindow::slotNetworkPrint() { statusMessage( tr("Printing...")); QPrintDialog dialog(printer, this); if ( dialog.exec() ) { QPainter painter(printer); graphicsWidget->render(&painter); }; statusMessage( tr("Ready.")); } /** * @brief MainWindow::slotNetworkImportGraphML * Imports a network from a GraphML formatted file */ void MainWindow::slotNetworkImportGraphML(){ bool m_checkSelectFileType = false; slotNetworkFileChoose( QString::null, FILE_GRAPHML, m_checkSelectFileType); } /** * @brief MainWindow::slotNetworkImportGML * Imports a network from a GML formatted file */ void MainWindow::slotNetworkImportGML(){ bool m_checkSelectFileType = false; slotNetworkFileChoose( QString::null, FILE_GML, m_checkSelectFileType); } /** * @brief MainWindow::slotNetworkImportPajek * Imports a network from a Pajek-like formatted file */ void MainWindow::slotNetworkImportPajek(){ bool m_checkSelectFileType = false; slotNetworkFileChoose( QString::null, FILE_PAJEK, m_checkSelectFileType); } /** * @brief MainWindow::slotNetworkImportSM * Imports a network from a Adjacency matrix formatted file */ void MainWindow::slotNetworkImportSM(){ bool m_checkSelectFileType = false; slotNetworkFileChoose( QString::null, FILE_ADJACENCY, m_checkSelectFileType); } /** * @brief MainWindow::slotNetworkImportDot * Imports a network from a Dot formatted file */ void MainWindow::slotNetworkImportDot(){ bool m_checkSelectFileType = false; slotNetworkFileChoose( QString::null ,FILE_GRAPHVIZ, m_checkSelectFileType); } /** * @brief MainWindow::slotNetworkImportDL * Imports a network from a UCINET formatted file */ void MainWindow::slotNetworkImportDL(){ bool m_checkSelectFileType = false; slotNetworkFileChoose( QString::null, FILE_UCINET, m_checkSelectFileType); } /** * @brief MainWindow::slotNetworkImportEdgeList * Imports a network from a simple List or weighted List formatted file */ void MainWindow::slotNetworkImportEdgeList(){ bool m_checkSelectFileType = false; switch( slotHelpMessageToUser(USER_MSG_QUESTION_CUSTOM, tr("Select type of edge list format..."), tr("Select type of edge list format"), tr("SocNetV can parse two kinds of edgelist formats: \n\n" "A. Edge lists with edge weights, " "where each line has exactly 3 columns: " "source target weight, i.e.:\n" "1 2 1 \n" "2 3 1 \n" "3 4 2 \n" "4 5 1 \n\n" "B. Simple edge lists without weights, where each line " "has two or more columns in the form: source, target1, target2, ... , i.e.:\n" "1 2 3 4 5 6\n" "2 3 4 \n" "3 5 8 7\n\n" "Please select the appropriate type of edge list format of " "the file you want to load:"), QMessageBox::NoButton, QMessageBox::NoButton, tr("Weighted"), tr("Simple non-weighted") ) ) { case 1: qDebug() << "*** MW::slotNetworkImportEdgeList - Weighted list selected! " ; slotNetworkFileChoose( QString::null, FILE_EDGELIST_WEIGHTED, m_checkSelectFileType); break; case 2: qDebug() << "*** MW: slotNetworkImportEdgeList - Simple list selected! " ; slotNetworkFileChoose( QString::null, FILE_EDGELIST_SIMPLE, m_checkSelectFileType); break; } } /** * @brief MainWindow::slotNetworkImportTwoModeSM * Imports a network from a two mode sociomatrix formatted file */ void MainWindow::slotNetworkImportTwoModeSM(){ bool m_checkSelectFileType = false; slotNetworkFileChoose( QString::null, FILE_TWOMODE, m_checkSelectFileType); } /** * @brief MainWindow::slotNetworkAvailableTextCodecs * Setup a list of all text codecs supported by current OS */ void MainWindow::slotNetworkAvailableTextCodecs() { QMap codecMap; QRegExp iso8859RegExp("ISO[- ]8859-([0-9]+).*"); foreach (int mib, QTextCodec::availableMibs()) { QTextCodec *codec = QTextCodec::codecForMib(mib); QString sortKey = codec->name().toUpper(); int rank; if (sortKey.startsWith("UTF-8")) { rank = 1; } else if (sortKey.startsWith("UTF-16")) { rank = 2; } else if (iso8859RegExp.exactMatch(sortKey)) { if (iso8859RegExp.cap(1).size() == 1) rank = 3; else rank = 4; } else { rank = 5; } sortKey.prepend(QChar('0' + rank)); codecMap.insert(sortKey, codec); } codecs = codecMap.values(); } /** * @brief MainWindow::slotNetworkFilePreview * @param m_fileName * @param m_fileFormat * @return * Called from slotNetworkFileChoose() * Opens a window to preview the selected file where the user * can select an appropriate text codec */ bool MainWindow::slotNetworkFilePreview(const QString &m_fileName, const int &m_fileFormat ){ qDebug() << "MW::slotNetworkFilePreview() - file: "<< m_fileName; if (!m_fileName.isEmpty()) { QFile file(m_fileName); if (!file.open(QFile::ReadOnly)) { slotHelpMessageToUserError( tr("Cannot read file %1:\n%2") .arg(m_fileName) .arg(file.errorString()) ); return false; } qDebug() << "MW::slotNetworkFilePreview() - reading file... " ; QByteArray data = file.readAll(); m_dialogPreviewFile->setEncodedData(data,m_fileName, m_fileFormat); m_dialogPreviewFile->exec(); } return true; } /** * @brief MainWindow::slotNetworkFileLoadRecent * Called on click on any file entry in "Recent Files" menu * Calls slotNetworkFileChoose() which checks file type and calls slotNetworkFilePreview */ void MainWindow::slotNetworkFileLoadRecent() { QAction *action = qobject_cast(sender()); if (action) { slotNetworkFileChoose(action->data().toString() ); } } /** * @brief MainWindow::slotNetworkFileLoad * @param m_fileName * @param m_codecName * @param m_fileFormat * @return * Main network file loader method * Called from m_dialogPreviewFile and slotNetworkDataSetRecreate * Calls initApp to init to default values. * Then calls activeGraph::graphLoad to actually load the network... */ void MainWindow::slotNetworkFileLoad(const QString m_fileName, const QString m_codecName, const int m_fileFormat ) { qDebug() << "MW::slotNetworkFileLoad() : "<< m_fileName << " m_codecName " << m_codecName << " m_fileFormat " << m_fileFormat; initApp(); userSelectedCodecName = m_codecName; //var for future use in a Settings dialog QString delimiter=QString::null; int two_sm_mode = 0; if ( m_fileFormat == FILE_TWOMODE ) { switch( slotHelpMessageToUser ( USER_MSG_QUESTION_CUSTOM, tr("Two-mode sociomatrix. Select mode..."), tr("Two-mode sociomatrix"), tr("If this file is in two-mode sociomatrix format, " "please specify which mode to open \n\n" "1st mode: rows are nodes \n" "2nd mode: columns are nodes"), QMessageBox::NoButton, QMessageBox::Ok, tr("1st Mode"),tr("2nd mode") ) ) { case 1: two_sm_mode = 1; break; case 2: two_sm_mode = 2; break; } } if ( m_fileFormat == FILE_EDGELIST_SIMPLE || m_fileFormat == FILE_EDGELIST_WEIGHTED ) { bool ok; QString delimiter = QInputDialog::getText(this, tr("Enter column delimiter"), tr("SocNetV supports edge list formatted files" "with arbitrary column delimiters. \n" "The default delimiter is one or more spaces.\n" "If the column delimiter in this file is " "other than simple space or TAB, \n" "please enter it below.\n" "For instance, if the delimiter is a " "comma or pipe enter \",\" or \"|\" respectively.\n" "Leave empty to use space or TAB as delimiter."), QLineEdit::Normal, QString(""), &ok); if (!ok || delimiter.isEmpty() || delimiter.isNull() ) { delimiter=" "; } qDebug()<<"MW::slotNetworkFileLoad() - delimiter" << delimiter; } QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); qDebug() << "MW::slotNetworkFileLoad() : calling activeGraph.graphLoad() "; activeGraph.graphLoad ( m_fileName, m_codecName, ((appSettings["initNodeLabelsVisibility"] == "true" ) ? true: false), m_fileFormat, two_sm_mode, delimiter ); QApplication::restoreOverrideCursor(); } /** * @brief MainWindow::slotNetworkFileLoaded * Called from Parser/Graph when a network file is loaded. * It informs the MW about the type of the network so that it can display the appropiate message. * @param type * @param netName * @param aNodes * @param totalEdges */ void MainWindow::slotNetworkFileLoaded (const int &type, const QString &fName, const QString &netName, const int &totalNodes, const int &totalEdges, const QString &message) { qDebug()<< "MW::slotNetworkFileLoaded() - type " << type; if (type > 0) { fileName=fName; previous_fileName=fileName; QFileInfo fileInfo (fileName); fileNameNoPath = fileInfo.fileName(); Q_ASSERT_X( !fileNameNoPath.isEmpty(), "not empty filename ", "empty filename " ); setWindowTitle("SocNetV "+ VERSION +" - "+fileNameNoPath); setLastPath(fileName); // store this path and file } else { qDebug()<< "MW::slotNetworkFileLoaded() - UNRECOGNIZED FILE. " "Message from Parser: " << message << "Calling initApp()"; statusMessage( tr("Error loading requested file. Aborted.")); slotHelpMessageToUser(USER_MSG_CRITICAL, tr("Error loading network file"), tr("Error loading network file"), tr("Sorry, the selected file is not in a supported format or encoding, " "or contains formatting errors. \n\n" "The error message was: \n\n" "%1" "\n\n" "What now? Review the message above to see if it helps you to fix the data file. " "Try a different codec in the preview window " "or if the file is of a legacy format (i.e. Pajek, UCINET, GraphViz, etc), " "please use the options in the Import sub menu. \n").arg(message) ); initApp(); return; } switch( type ) { case 0: break; case 1: statusMessage( tr("GraphML formatted network, named %1, loaded with %2 Nodes and %3 total Edges.").arg( netName ).arg( totalNodes ).arg(totalEdges ) ); break; case 2: statusMessage( QString(tr("Pajek formatted network, named %1, loaded with %2 Nodes and %3 total Edges.")).arg( netName ).arg( totalNodes ).arg(totalEdges )); break; case 3: statusMessage( QString(tr("Adjacency formatted network, named %1, loaded with %2 Nodes and %3 total Edges.")).arg( netName ).arg( totalNodes ).arg(totalEdges ) ); break; case 4: statusMessage( QString(tr("GraphViz (Dot) formatted network, named %1, loaded with %2 Nodes and %3 total Edges.")).arg( netName ).arg( totalNodes ).arg(totalEdges ) ); break; case 5: statusMessage( QString(tr("UCINET formatted network, named %1, loaded with %2 Nodes and %3 total Edges.")).arg( netName ).arg( totalNodes ).arg(totalEdges ) ); break; case 6: statusMessage( QString(tr("GML formatted network, named %1, loaded with %2 Nodes and %3 total Edges.")).arg( netName ).arg( totalNodes ).arg(totalEdges ) ); break; case 7: statusMessage( QString(tr("Weighted list formatted network, named %1, loaded with %2 Nodes and %3 total Edges.")).arg( netName ).arg( totalNodes ).arg(totalEdges ) ); break; case 8: statusMessage( QString(tr("Simple list formatted network, named %1, loaded with %2 Nodes and %3 total Edges.")).arg( netName ).arg( totalNodes ).arg(totalEdges ) ); break; case 9: statusMessage( QString(tr("Two-mode affiliation network, named %1, loaded with %2 Nodes and %3 total Edges.")).arg( netName ).arg( totalNodes ).arg(totalEdges ) ); break; default: // just for sanity QMessageBox::critical(this, "Error","Unrecognized format. \nPlease specify" " which is the file-format using Import Menu.","OK",0); break; } networkSave->setIcon(QIcon(":/images/saved.png")); networkSave->setEnabled(false); } /** * @brief MainWindow::slotEditRelationsClear * Called from Graph::relationsClear() to clear the relations combo. */ void MainWindow::slotEditRelationsClear(){ qDebug() << "MW::slotEditRelationsClear() - clearing combo"; editRelationChangeCombo->clear(); } /** * @brief MainWindow::slotEditRelationAdd * Called from MW when user clicks New Relation btn * or when the user creates the first edge visually. * * called from activeGraph::relationAdd(QString) * via signal Graph::signalRelationChangedToMW() when the parser or a * Graph method demands a new relation to be added in the Combobox. * */ void MainWindow::slotEditRelationAdd(QString newRelationName, const bool &changeRelation){ int comboItemsBefore = editRelationChangeCombo->count(); int relationsCounter=activeGraph.relations(); qDebug() << "MW::slotEditRelationAdd() - adding relation:" << newRelationName <<"to relations combo. Before this, combo items:" << comboItemsBefore << "and currentIndex:" <currentIndex() << "relationsCounter:" <addItem(newRelationName); if (changeRelation) { if ( comboItemsBefore == 0 ) { // only at startup slotEditRelationChange(0); } else { slotEditRelationChange(); } } qDebug() << "MW::slotEditRelationAdd() - added relation:" << newRelationName <<"now combo items:" << editRelationChangeCombo->count() << "now currentIndex:" <currentIndex() << "relationsCounter" <setCurrentIndex( ( editRelationChangeCombo->count()-1 ) ); } else { qDebug() << "MW::slotEditRelationChange(int) - to index" << relIndex; editRelationChangeCombo->setCurrentIndex(relIndex); } } /** * @brief MainWindow::slotEditRelationRename * @param newName */ void MainWindow::slotEditRelationRename(QString newName) { qDebug()<<"MW::slotEditRelationRename() -" << newName; bool ok=false; if (newName.isNull() || newName.isEmpty()) { qDebug()<<"MW::slotEditRelationRename() - prompt to enter new name"; newName = QInputDialog::getText( this, tr("Rename current relation"), tr("Enter a new name for this relation."), QLineEdit::Normal, QString::null, &ok ); if ( newName.isEmpty() || !ok ){ slotHelpMessageToUser(USER_MSG_CRITICAL, tr("Not a valid name."), tr("Error"), tr("You did not enter a valid name for this relation.") ); return; } else { activeGraph.relationCurrentRename(newName, true); } } else { qDebug()<<"MW::slotEditRelationRename() - current text " << editRelationChangeCombo->currentText(); qDebug()<<"MW::slotEditRelationRename() - updating combo name to" << newName; editRelationChangeCombo->setCurrentText(newName); } } /** * @brief MainWindow::slotExportPNG * @return * Exports the network to a PNG image - Mediocre Quality but smaller file */ bool MainWindow::slotNetworkExportPNG(){ qDebug()<< "MW::slotNetworkExportPNG"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return false; } QString fn = QFileDialog::getSaveFileName( this,tr("Save"), getLastPath(), tr("Image Files (*.png)")); if (fn.isEmpty()) { statusMessage( tr("Saving aborted") ); return false; } setLastPath(fn); // store this path tempFileNameNoPath=fn.split ("/"); qDebug("slotExportPNG: grabbing canvas"); QPixmap picture; picture=QPixmap::grabWidget(graphicsWidget, graphicsWidget->rect()); qDebug("slotExportPNG: adding logo"); QPainter p; p.begin(&picture); p.setFont(QFont ("Helvetica", 10, QFont::Normal, false)); if (appSettings["printLogo"]=="true") { QImage logo(":/images/socnetv-logo.png"); p.drawImage(5,5, logo); p.drawText(7,47,tempFileNameNoPath.last()); } else p.drawText(5,15,tempFileNameNoPath.last()); p.end(); qDebug("slotExportPNG: checking filename"); if (fn.contains("png", Qt::CaseInsensitive) ) { picture.toImage().save(fn, "PNG"); QMessageBox::information(this, "Export to PNG...", tr("Image Saved as: ")+tempFileNameNoPath.last(), "OK",0); } else { picture.toImage().save(fn+".png", "PNG"); QMessageBox::information(this, "Export to PNG...", tr("Image Saved as: ")+tempFileNameNoPath.last()+".png" , "OK",0); } statusMessage( tr("Exporting completed") ); return true; } /** * @brief MainWindow::slotNetworkExportBMP * @return * Exports the network to a BMP image - Better Quality but larger file */ bool MainWindow::slotNetworkExportBMP(){ qDebug( "slotNetworkExportBMP()"); if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return false; } QString format="bmp"; QString fn = QFileDialog::getSaveFileName( this,tr("Save Image as"), getLastPath(),tr("Image Files (*.bmp)")); if (fn.isEmpty()) { statusMessage( tr("Saving aborted") ); return false; } setLastPath(fn); // store this path tempFileNameNoPath=fn.split ("/"); QPixmap picture; qDebug("slotNetworkExportBMP: grabbing canvas"); picture=QPixmap::grabWidget(graphicsWidget, graphicsWidget->viewport()->rect()); QPainter p; qDebug("slotNetworkExportBMP: adding logo"); p.begin(&picture); p.setFont(QFont ("Helvetica", 10, QFont::Normal, false)); if (appSettings["printLogo"]=="true") { QImage logo(":/images/socnetv-logo.png"); p.drawImage(5,5, logo); p.drawText(7,47,tempFileNameNoPath.last()); } else p.drawText(5,15,tempFileNameNoPath.last()); p.end(); qDebug("slotNetworkExportBMP: checking file"); if (fn.contains(format, Qt::CaseInsensitive) ) { picture.toImage().save(fn, format.toLatin1()); QMessageBox::information(this, tr("Export to BMP..."), tr("Image Saved as: ")+tempFileNameNoPath.last(), "OK",0); } else { picture.toImage().save(fn+"."+format, format.toLatin1()); QMessageBox::information(this, tr("Export to BMP..."), tr("Image Saved as: ")+tempFileNameNoPath.last()+"."+format , "OK",0); } qDebug()<< "Exporting BMP to "<< fn; statusMessage( tr("Exporting completed") ); qDebug("Export finished!"); return true; } /** * @brief MainWindow::slotExportPDF * @return * Exports the network to a PDF Document - Best Quality */ bool MainWindow::slotNetworkExportPDF(){ qDebug()<< "MW::slotNetworkExportPDF()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return false; } QString m_fileName = QFileDialog::getSaveFileName( this, tr("Export to PDF"), getLastPath(), tr("Portable Document Format files (*.pdf)")); if (m_fileName.isEmpty()) { statusMessage( tr("Saving aborted")); return false; } else { if (QFileInfo(m_fileName).suffix().isEmpty()) m_fileName.append(".pdf"); // dont set to HighResolution - it breaks pdf export QPrinter printer(QPrinter::ScreenResolution); printer.setOutputFormat(QPrinter::PdfFormat); printer.setOutputFileName(m_fileName); QPainter p; p.begin(&printer); graphicsWidget->render(&p); p.end(); } qDebug()<< "Exporting PDF to "<< m_fileName; tempFileNameNoPath=m_fileName.split ("/"); setLastPath(m_fileName); QMessageBox::information(this, tr("Export to PDF..."), tr("File saved as: ")+tempFileNameNoPath.last() , "OK",0); statusMessage( tr("Exporting completed") ); return true; } /** * @brief MainWindow::slotExportPajek * Exports the network to a Pajek-formatted file * Calls the relevant Graph method. */ void MainWindow::slotNetworkExportPajek() { qDebug () << "MW::slotNetworkExportPajek"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } statusMessage( tr("Exporting active network under new filename...")); QString fn = QFileDialog::getSaveFileName( this, tr("Export Network to File Named..."), getLastPath(), tr("Pajek (*.paj *.net *.pajek);;All (*)") ); if (!fn.isEmpty()) { if ( QFileInfo(fn).suffix().isEmpty() ){ QMessageBox::information(this, "Missing Extension ", tr("File extension was missing! \n" "Appending a standard .paj to the given filename."), "OK",0); fn.append(".paj"); } fileName=fn; setLastPath(fileName); QFileInfo fileInfo (fileName); fileNameNoPath = fileInfo.fileName(); } else { statusMessage( tr("Saving aborted")); return; } activeGraph.graphSave(fileName, FILE_PAJEK); } /** * @brief MainWindow::slotNetworkExportSM * Exports the network to a adjacency matrix-formatted file * Calls the relevant Graph method. */ void MainWindow::slotNetworkExportSM(){ qDebug("MW: slotNetworkExportSM()"); if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } statusMessage( tr("Exporting active network under new filename...")); QString fn = QFileDialog::getSaveFileName( this, tr("Export Network to File Named..."), getLastPath(), tr("Adjacency (*.adj *.sm *.txt *.csv *.net);;All (*)") ); if (!fn.isEmpty()) { if ( QFileInfo(fn).suffix().isEmpty() ){ QMessageBox::information(this, "Missing Extension ", tr("File extension was missing! \n" "Appending a standard .adj to the given filename."), "OK",0); fn.append(".adj"); } fileName=fn; setLastPath(fileName); QFileInfo fileInfo (fileName); fileNameNoPath = fileInfo.fileName(); } else { statusMessage( tr("Saving aborted")); return; } bool saveEdgeWeights=false; if (activeGraph.graphWeighted() ) { switch ( slotHelpMessageToUser(USER_MSG_QUESTION, tr("Weighted graph. Social network with valued/weighted edges"), tr("Social network with valued/weighted edges"), tr("This social network includes valued/weighted edges " "(the depicted graph is weighted). " "Do you want to save the edge weights in the adjacency file?\n" "Select Yes if you want to save edge values " "in the resulting file. \n" "Select No, if you don't want edge values " "to be saved. In the later case, all non-zero values will be truncated to 1.") ) ) { case QMessageBox::Yes: saveEdgeWeights = true; break; case QMessageBox::No: saveEdgeWeights = false; break; case QMessageBox::Cancel: statusMessage( tr("Save aborted...") ); return; break; } } activeGraph.graphSave(fileName, FILE_ADJACENCY, saveEdgeWeights ) ; } /** * @brief MainWindow::slotNetworkExportDL * @return Exports the network to a DL-formatted file * - TODO slotNetworkExportDL */ bool MainWindow::slotNetworkExportDL(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return false; } if (fileName.isEmpty()) { statusMessage( tr("Saving network under new filename...")); QString fn = QFileDialog::getSaveFileName( this, "Export UCINET", getLastPath(), 0); if (!fn.isEmpty()) { fileName=fn; setLastPath(fileName); } else { statusMessage( tr("Saving aborted")); return false; } } return true; } /** Exports the network to a GW-formatted file TODO slotNetworkExportGW */ bool MainWindow::slotNetworkExportGW(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return false; } if (fileName.isEmpty()) { statusMessage( tr("Saving network under new filename...")); QString fn = QFileDialog::getSaveFileName( this, "Export GW", getLastPath(), 0); if (!fn.isEmpty()) { fileName=fn; setLastPath(fileName); } else { statusMessage( tr("Saving aborted")); return false; } } return true; } /** Exports the network to a list-formatted file TODO slotNetworkExportList */ bool MainWindow::slotNetworkExportList(){ if (fileName.isEmpty()) { statusMessage( tr("Saving network under new filename...")); QString fn = QFileDialog::getSaveFileName( this, "Export List", getLastPath(), 0); if (!fn.isEmpty()) { fileName=fn; setLastPath(fileName); } else { statusMessage( tr("Saving aborted")); return false; } } return true; } /** * @brief Displays the file of the loaded network. Network _must_ be unchanged since last save/load. Otherwise it will ask the user to first save the network, then view its file. */ void MainWindow::slotNetworkFileView(){ qDebug() << "slotNetworkFileView() : " << fileName.toLatin1(); if ( activeGraph.graphLoaded() && activeGraph.graphSaved() ) { //network unmodified, read loaded file again. QFile f( fileName ); if ( !f.open( QIODevice::ReadOnly ) ) { qDebug ("Error in open!"); return; } TextEditor *ed = new TextEditor(fileName,this,false); QFileInfo fileInfo (fileName); fileNameNoPath = fileInfo.fileName(); ed->setWindowTitle( fileNameNoPath ); ed->show(); m_textEditors << ed; statusMessage( tr("Displaying network data file %1" ).arg(fileNameNoPath)); } else if (!activeGraph.graphSaved() ) { if ( !activeGraph.graphLoaded() ) { // new network, not saved yet int response = slotHelpMessageToUser(USER_MSG_QUESTION, tr("New network not saved yet. You might want to save it first."), tr("This new network you created has not been saved yet."), tr("Do you want to open a file dialog to save your work " "(then I will display the file)?"), QMessageBox::Yes|QMessageBox::No,QMessageBox::Yes ); if ( response == QMessageBox::Yes ) { slotNetworkSaveAs(); } else { return; } } else { // loaded network, but modified int response = slotHelpMessageToUser(USER_MSG_QUESTION, tr("Current network has been modified. Save to the original file?"), tr("Current social network has been modified since last save."), tr("Do you want to save it to the original file?"), QMessageBox::Yes|QMessageBox::No,QMessageBox::Yes ); if ( response == QMessageBox::Yes ){ slotNetworkSave(); }else if (response ==QMessageBox::No ) { slotNetworkSaveAs(); } else { // user pressed Cancel return; } } slotNetworkFileView(); } else { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); } } /** * @brief Opens the embedded text editor */ void MainWindow::slotNetworkTextEditor(){ qDebug() << "slotNetworkTextEditor() : "; TextEditor *ed = new TextEditor("", this,false); ed->setWindowTitle(tr("New Network File")); ed->show(); m_textEditors << ed; statusMessage( tr("Enter your network data here" ) ); } /** * @brief Displays the adjacency matrix of the network. * It uses a different method for writing the matrix to a file. * While slotNetworkExportSM uses << operator of Matrix class * (via adjacencyMatrix of Graph class), this is using directly the * writeMatrixAdjacency method of Graph class */ void MainWindow::slotNetworkViewSociomatrix(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-matrix-adjacency-"+dateTime+".html"; qDebug () << "MW::slotNetworkViewSociomatrix() - dataDir" << appSettings["dataDir"] << "fn" <show(); m_textEditors << ed; } statusMessage(tr("Adjacency matrix saved as ") + QDir::toNativeSeparators(fn)); } /** * @brief Displays a text-only plot of the network adjacency matrix */ void MainWindow::slotNetworkViewSociomatrixPlotText(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int N=activeNodes(); statusMessage(tr("Creating plot of adjacency matrix of %1 nodes.").arg(N )); qDebug ("MW: calling Graph::writeMatrixAdjacency with %i nodes", N); QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-matrix-adjacency-plot-"+dateTime+".html"; bool simpler = false; if ( N > 999 ) { float MB = (N * N * 10)/(1024*1024); switch ( slotHelpMessageToUser( USER_MSG_QUESTION,tr("Very large network to plot!"), tr("Warning: Really large network"), tr("To plot a %1 x %1 matrix arranged in HTML table, " "I will need time to write a very large .html file , circa %2 MB in size. " "Instead, I can create a simpler / smaller HTML file without table. " "Press Yes to continue with simpler version, " "Press No to create large file with HTML table.").arg(N).arg( MB ) ) ) { case QMessageBox::Yes: simpler = true; break; case QMessageBox::No: simpler = false; break; default: return; break; } } createProgressBar(0,progressMsg); activeGraph.writeMatrixAdjacencyPlot(fn, simpler); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Visual form of adjacency matrix saved as ") + QDir::toNativeSeparators(fn)); } /** * @brief MainWindow::slotNetworkDataSetSelect * Calls the m_datasetSelectionDialog to display the dataset selection dialog */ void MainWindow::slotNetworkDataSetSelect(){ qDebug()<< "MW::slotNetworkDataSetSelect()"; m_datasetSelectDialog.exec(); } /** * @brief MainWindow::slotNetworkDataSetRecreate * @param m_fileName * Recreates some of the most famous and widely used data sets in * network analysis studies */ void MainWindow::slotNetworkDataSetRecreate (const QString m_fileName) { int m_fileFormat=0; qDebug()<< "MW::slotNetworkDataSetRecreate() fileName: " << m_fileName; //initApp(); qDebug()<< "MW::slotNetworkDataSetRecreate() datadir+fileName: " << appSettings["dataDir"]+m_fileName; activeGraph.writeDataSetToFile(appSettings["dataDir"], m_fileName); if (m_fileName.endsWith(".graphml")) { m_fileFormat=FILE_GRAPHML; } else if (m_fileName.endsWith(".pajek") || m_fileName.endsWith(".paj") || m_fileName.endsWith(".net")) { m_fileFormat=FILE_PAJEK; } else if (m_fileName.endsWith(".sm") || m_fileName.endsWith(".adj")) { m_fileFormat=FILE_ADJACENCY; } else if (m_fileName.endsWith(".dot")) { m_fileFormat=FILE_GRAPHVIZ; } else if (m_fileName.endsWith(".dl")) { m_fileFormat=FILE_UCINET; } else if (m_fileName.endsWith(".gml")) { m_fileFormat=FILE_GML; } else if (m_fileName.endsWith(".wlst")) { m_fileFormat=FILE_EDGELIST_WEIGHTED; } else if (m_fileName.endsWith(".lst")) { m_fileFormat=FILE_EDGELIST_SIMPLE; } else if (m_fileName.endsWith(".2sm")) { m_fileFormat=FILE_TWOMODE; } slotNetworkFileLoad(appSettings["dataDir"]+m_fileName, "UTF-8", m_fileFormat); // if ( slotNetworkFileLoad(appSettings["dataDir"]+m_fileName, "UTF-8", m_fileFormat) ) { // qDebug() << "slotNetworkDataSetRecreate() loaded file " << m_fileName; // fileName=m_fileName; // previous_fileName=fileName; // setWindowTitle("SocNetV "+ VERSION +" - "+fileName); // QString message=tr("Dataset loaded. Dataset file saved as ") + fileName; // statusMessage( message ); // } // else { // statusMessage( "Could not read new network data file. Aborting."); // } } /** * @brief MainWindow::slotNetworkRandomErdosRenyiDialog * Shows the Erdos-Renyi network creation dialog */ void MainWindow::slotNetworkRandomErdosRenyiDialog(){ statusMessage( tr("Generate a random Erdos-Renyi network. ")); m_randErdosRenyiDialog = new DialogRandErdosRenyi( this, appSettings["randomErdosEdgeProbability"].toFloat(0)); connect( m_randErdosRenyiDialog, &DialogRandErdosRenyi::userChoices, this, &MainWindow::slotNetworkRandomErdosRenyi ); m_randErdosRenyiDialog->exec(); } /** * @brief MainWindow::slotNetworkRandomErdosRenyi * @param newNodes * @param model * @param edges * @param eprob * @param mode * @param diag * Calls activeGraph.slotNetworkRandomErdosRenyi () to create a symmetric network * Edge existance is controlled by a user specified possibility. */ void MainWindow::slotNetworkRandomErdosRenyi( const int newNodes, const QString model, const int edges, const float eprob, const QString mode, const bool diag) { qDebug() << "MW::slotNetworkRandomErdosRenyi()"; statusMessage( tr("Erasing any existing network.")); initApp(); statusMessage( tr("Creating Erdos-Renyi Random Network. Please wait... ") ); progressMsg = "Creating Erdos-Renyi Random Network. \n" " Please wait (or disable progress bars from Options -> Settings)."; createProgressBar( (edges != 0 ? edges:newNodes), progressMsg ); appSettings["randomErdosEdgeProbability"] = QString::number(eprob); activeGraph.randomNetErdosCreate ( newNodes, model, edges, eprob, mode, diag); destroyProgressBar( (edges != 0 ? edges:newNodes) ); setWindowTitle("Untitled Erdos-Renyi random network"); double threshold = log(newNodes)/newNodes; //float clucof=activeGraph.clusteringCoefficient(); if ( (eprob ) > threshold ) QMessageBox::information( this, "New Erdos-Renyi Random Network", tr("Random network created. \n")+ //tr("\nAverage path length: ") + QString::number(avGraphDistance)+ //tr("\nClustering coefficient: ")+QString::number(clucof)+ tr("\n\nOn the average, edges should be ") + QString::number( eprob * newNodes*(newNodes-1)) + tr("\nThis graph is almost surely connected because: \nprobability > ln(n)/n, that is: \n") + QString::number(eprob)+ tr(" bigger than ")+ QString::number(threshold) , "OK",0); else QMessageBox::information( this, "New Erdos-Renyi Random Network", tr("Random network created. \n")+ //tr("\nAverage path length: ") + QString::number(avGraphDistance)+ //tr("\nClustering coefficient: ")+QString::number(clucof)+ tr("\n\nOn the average, edges should be ") + QString::number(eprob * newNodes*(newNodes-1)) + tr("\nThis graph is almost surely not connected because: \nprobability < ln(n)/n, that is: \n") + QString::number(eprob)+ " smaller than "+ QString::number(threshold) , "OK",0); statusMessage( tr("Erdos-Renyi Random Network created. ") ) ; } /** * @brief MainWindow::slotNetworkRandomScaleFreeDialog */ void MainWindow::slotNetworkRandomScaleFreeDialog() { qDebug() << "MW::slotNetworkRandomScaleFreeDialog()"; statusMessage( tr("Generate a random Scale-Free network. ")); m_randScaleFreeDialog = new DialogRandScaleFree(this); connect( m_randScaleFreeDialog, &DialogRandScaleFree::userChoices, this, &MainWindow::slotNetworkRandomScaleFree); m_randScaleFreeDialog->exec(); } /** * @brief MainWindow::slotNetworkRandomScaleFree * @param nodes * @param power * @param initialNodes * @param edgesPerStep * @param zeroAppeal * @param mode */ void MainWindow::slotNetworkRandomScaleFree ( const int &newNodes, const int &power, const int &initialNodes, const int &edgesPerStep, const float &zeroAppeal, const QString &mode) { qDebug() << "MW::slotNetworkRandomScaleFree()"; statusMessage( tr("Erasing any existing network. ")); initApp(); statusMessage( tr("Creating Scale-Free Random Network. Please wait...")); progressMsg = "Creating Scale-Free Random Network. \n" "Please wait (or disable progress bars from Options -> Settings)."; createProgressBar(newNodes, progressMsg ); activeGraph.randomNetScaleFreeCreate( newNodes, power, initialNodes, edgesPerStep, zeroAppeal, mode); destroyProgressBar(newNodes); setWindowTitle("Untitled scale-free network"); //float avGraphDistance=activeGraph.graphDistanceGeodesicAverage(); //float clucof=activeGraph.clusteringCoefficient(); QMessageBox::information(this, "New scale-free network", tr("Scale-free random network created.\n") // +tr("\nNodes: ")+ QString::number(nodesSelected)+ // tr("\nEdges: ") + QString::number( edgeCount ) //+ tr("\nAverage path length: ") + QString::number(avGraphDistance) //+ tr("\nClustering coefficient: ")+QString::number(clucof) , "OK",0); statusMessage( tr("Scale-Free Random Network created. ") ); } /** * @brief MainWindow::slotNetworkRandomSmallWorldDialog */ void MainWindow::slotNetworkRandomSmallWorldDialog() { qDebug() << "MW::slotNetworkRandomSmallWorldDialog()"; statusMessage( tr("Generate a random Small-World network. ")); m_randSmallWorldDialog = new DialogRandSmallWorld(this); connect( m_randSmallWorldDialog, &DialogRandSmallWorld::userChoices, this, &MainWindow::slotNetworkRandomSmallWorld); m_randSmallWorldDialog->exec(); } /** * @brief MainWindow::slotNetworkRandomSmallWorld * @param nodes * @param degree * @param beta * @param mode * @param diag */ void MainWindow::slotNetworkRandomSmallWorld(const int &newNodes, const int °ree, const float &beta, const QString &mode, const bool &diag) { Q_UNUSED(diag); qDebug() << "MW::slotNetworkRandomSmallWorld()"; statusMessage( tr("Erasing any existing network. ")); initApp(); statusMessage( tr("Creating Small-World Random Network. Please wait...")); progressMsg = "Creating Small-World Random Network. \n" "Please wait (or disable progress bars from Options -> Settings)."; createProgressBar(newNodes, progressMsg ); activeGraph.randomNetSmallWorldCreate(newNodes, degree, beta, mode); destroyProgressBar(newNodes); setWindowTitle("Untitled small-world network"); //float avGraphDistance=activeGraph.graphDistanceGeodesicAverage(); //float clucof=activeGraph.clusteringCoefficient(); QMessageBox::information(this, "New Small World network", tr("Small world network created.\n") // +tr("\nNodes: ")+ QString::number(nodeCount)+ // tr("\nEdges: ") + QString::number( edgeCount ) //+ tr("\nAverage path length: ") + QString::number(avGraphDistance) //+ tr("\nClustering coefficient: ")+QString::number(clucof) , "OK",0); statusMessage( tr("Small World Random Network created. ") ); } /** * @brief MainWindow::slotNetworkRandomRegularDialog */ void MainWindow::slotNetworkRandomRegularDialog() { qDebug() << "MW::slotRandomRegularDialog()"; statusMessage( tr("Generate a d-regular random network. ")); m_randRegularDialog = new DialogRandRegular(this); connect( m_randRegularDialog, &DialogRandRegular::userChoices, this, &MainWindow::slotNetworkRandomRegular); m_randRegularDialog->exec(); } /** * @brief MainWindow::slotNetworkRandomRegular * Creates a pseudo-random k-regular network where every node has the same degree */ void MainWindow::slotNetworkRandomRegular(const int &newNodes, const int °ree, const QString &mode, const bool &diag){ statusMessage( "Erasing any existing network. "); initApp(); statusMessage( "Creating a pseudo-random d-regular network where each node " "has the same degree... "); progressMsg = "Creating pseudo-random d-regular network. \n" "Please wait (or disable progress bars from Options -> Settings)."; createProgressBar(newNodes, progressMsg ); activeGraph.randomNetRegularCreate (newNodes,degree, mode, diag); destroyProgressBar(newNodes); setWindowTitle("Untitled d-regular network"); //float avGraphDistance=activeGraph.graphDistanceGeodesicAverage(); //float clucof=activeGraph.clusteringCoefficient(); QMessageBox::information(this, "New d-Regular network", tr("d-Regular network created.\n") // +tr("\nNodes: ")+ QString::number(nodeCount)+ // tr("\nEdges: ") + QString::number( edgeCount ) //+ tr("\nAverage path length: ") + QString::number(avGraphDistance) //+ tr("\nClustering coefficient: ")+QString::number(clucof) , "OK",0); statusMessage( tr( "d-regular network created. " ) ); } void MainWindow::slotNetworkRandomGaussian(){ } /** * @brief MainWindow::slotNetworkRandomRingLattice * Creates a lattice network, i.e. a connected network where every node has the same degree and is connected with its neighborhood. */ void MainWindow::slotNetworkRandomRingLattice(){ bool ok; statusMessage( "You have selected to create a ring lattice network. "); int newNodes=( QInputDialog::getInt( this, tr("Create ring lattice"), tr("This will create a ring lattice network, " "where each node has degree d:\n d/2 edges to the right " "and d/2 to the left.\n" "Please enter the number of nodes you want:"), 100, 4, maxNodes, 1, &ok ) ) ; if (!ok) { statusMessage( "You did not enter an integer. Aborting."); return; } int degree = QInputDialog::getInt( this, tr("Create ring lattice..."), tr("Now, enter an even number d. \n" "This is the total number of edges each new node will have:"), 2, 2, newNodes-1, 2, &ok); if ( (degree% 2)==1 ) { QMessageBox::critical(this, "Error",tr(" Sorry. I cannot create such a network. " "Degree must be even number"), "OK",0); return; } statusMessage( "Erasing any existing network. "); initApp(); statusMessage( "Creating ring lattice network. Please wait..."); progressMsg = "Creating ring-lattice network. \n" "Please wait (or disable progress bars from Options -> Settings)."; createProgressBar(newNodes, progressMsg ); activeGraph.randomNetRingLatticeCreate(newNodes, degree, true ); destroyProgressBar(newNodes); setWindowTitle("Untitled ring-lattice network"); //float avGraphDistance=activeGraph.graphDistanceGeodesicAverage(); //float clucof=activeGraph.clusteringCoefficient(); QMessageBox::information(this, "New Ring Lattice", tr("Ring lattice network created.\n") // +tr("\nNodes: ")+ QString::number(activeNodes())+ // tr("\nEdges: ")+ QString::number( activeEdges() ) // + tr("\nAverage path length: ") + QString::number(avGraphDistance) //+ tr("\nClustering coefficient: ")+QString::number(clucof) , "OK",0); statusMessage( tr("Ring lattice random network created: " )); } /** * @brief MainWindow::slotNetworkWebCrawlerDialog * Shows a dialog where enters a website url * and the app creates a new network by crawling it */ void MainWindow::slotNetworkWebCrawlerDialog() { qDebug () << "MW: slotNetworkWebCrawlerDialog() - canvas Width & Height already sent"; m_WebCrawlerDialog.exec() ; } /** * @brief MainWindow::slotNetworkWebCrawler * Called from m_WebCrawlerDialog * Clears the loaded network (saving if needed) then passes parameters to * Graph::webCrawl function * @param seed * @param maxNodes * @param maxRecursion * @param extLinks * @param intLinks */ void MainWindow::slotNetworkWebCrawler ( QString seed, int maxNodes, int maxRecursion, bool extLinks, bool intLinks) { this->slotNetworkClose(); activeGraph.webCrawl( seed, maxNodes, maxRecursion, extLinks, intLinks) ; } /** * @brief MainWindow::slotNetworkChanged * Activated when something has been changed in the graph. * Makes the networkSave icon active and refreshes any LCD values. * Also called from activeGraph and graphicsWidget. */ void MainWindow::slotNetworkChanged(const int &graphStatus, const bool &undirected, const int &vertices, const int &edges, const float &density){ qDebug()<<"MW::slotNetworkChanged()" <<"graphStatus" << graphStatus <<"undirected" << undirected <<"vertices" << vertices <<"edges" <setIcon(QIcon(":/images/save.png")); networkSave->setEnabled(true); } rightPanelNodesLCD->display(vertices); if ( undirected ) { rightPanelEdgesLCD->setStatusTip(tr("Shows the total number of undirected edges in the network.")); rightPanelEdgesLCD->setToolTip(tr("The total number of undirected edges in the network.")); rightPanelNetworkTypeLabel->setStatusTip(tr("Undirected data mode. Toggle the menu option Edit -> Edges -> Undirected Edges to change it")); rightPanelNetworkTypeLabel->setToolTip(tr("The loaded network, if any, is undirected and \n" "any edge you add between nodes will be undirected.\n" "If you want to work with directed edges and/or \n" "transform the loaded network (if any) to directed \n" "disable the option Edit -> Edges -> Undirected \n" "or press CTRL+E+U")); rightPanelNetworkTypeLabel->setWhatsThis(tr("The loaded network, if any, is undirected and \n" "any edge you add between nodes will be undirected.\n" "If you want to work with directed edges and/or \n" "transform the loaded network (if any) to directed \n" "disable the option Edit -> Edges -> Undirected \n" "or press CTRL+E+U")); if (toolBoxEditEdgeModeSelect->currentIndex()==0) { toolBoxEditEdgeModeSelect->setCurrentIndex(1); } rightPanelNetworkTypeLabel-> setText ("Network Type: Undirected"); rightPanelEdgesLabel->setText(tr("Total Edges")); rightPanelSelectedEdgesLabel->setText(tr("Selected Edges")); editEdgeUndirectedAllAct->setChecked(true); } else { rightPanelEdgesLCD->setStatusTip(tr("Shows the total number of directed edges in the network.")); rightPanelEdgesLCD->setToolTip(tr("The total number of directed edges in the network.")); rightPanelNetworkTypeLabel->setStatusTip(tr("Directed data mode. Toggle the menu option Edit -> Edges -> Undirected Edges to change it")); rightPanelNetworkTypeLabel->setToolTip(tr("The loaded network, if any, is directed and \n" "any link you add between nodes will be a directed arc.\n" "If you want to work with undirected edges and/or \n" "transform the loaded network (if any) to undirected \n" "enable the option Edit -> Edges -> Undirected \n" "or press CTRL+E+U")); rightPanelNetworkTypeLabel->setWhatsThis(tr("The loaded network, if any, is directed and \n" "any link you add between nodes will be a directed arc.\n" "If you want to work with undirected edges and/or \n" "transform the loaded network (if any) to undirected \n" "enable the option Edit -> Edges -> Undirected \n" "or press CTRL+E+U")); rightPanelNetworkTypeLabel-> setText ("Network Type: Directed"); if (toolBoxEditEdgeModeSelect->currentIndex()==1) { toolBoxEditEdgeModeSelect->setCurrentIndex(0); } rightPanelEdgesLabel->setText(tr("Total Arcs")); rightPanelSelectedEdgesLabel->setText(tr("Selected Arcs")); editEdgeUndirectedAllAct->setChecked(false); } rightPanelEdgesLCD->display(edges); rightPanelDensityLCD->display( density ); } /** * @brief MainWindow::slotEditOpenContextMenu * Popups a context menu with some options when the user right-clicks on the scene * @param mPos */ void MainWindow::slotEditOpenContextMenu( const QPointF &mPos) { Q_UNUSED(mPos); QMenu *contextMenu = new QMenu(" Menu",this); Q_CHECK_PTR( contextMenu ); //displays "out of memory" if needed int nodesSelected = activeGraph.graphSelectedVerticesCount(); contextMenu -> addAction( "## Selected nodes: " + QString::number( nodesSelected ) + " ## "); contextMenu -> addSeparator(); if (nodesSelected > 0) { contextMenu -> addAction(editNodePropertiesAct ); contextMenu -> addSeparator(); contextMenu -> addAction(editNodeRemoveAct ); if (nodesSelected > 1 ){ editNodeRemoveAct->setText(tr("Remove ") + QString::number(nodesSelected) + tr(" nodes")); contextMenu -> addSeparator(); contextMenu -> addAction(editNodeSelectedToCliqueAct); contextMenu -> addAction(editNodeSelectedToStarAct); contextMenu -> addAction(editNodeSelectedToCycleAct); contextMenu -> addAction(editNodeSelectedToLineAct); } else { editNodeRemoveAct->setText(tr("Remove ") + QString::number(nodesSelected) + tr(" node")); } contextMenu -> addSeparator(); } contextMenu -> addAction( editNodeAddAct ); contextMenu -> addSeparator(); contextMenu -> addAction( editEdgeAddAct ); contextMenu -> addSeparator(); QMenu *options=new QMenu("Options", this); contextMenu -> addMenu(options ); options -> addAction (openSettingsAct ); options -> addSeparator(); options -> addAction (editNodeSizeAllAct ); options -> addAction (editNodeShapeAll ); options -> addAction (editNodeColorAll ); options -> addAction (optionsNodeNumbersVisibilityAct); options -> addAction (optionsNodeLabelsVisibilityAct); options -> addSeparator(); options -> addAction (editEdgeColorAllAct ); options -> addSeparator(); options -> addAction (changeBackColorAct ); options -> addAction (backgroundImageAct ); //QCursor::pos() is good only for menus not related with node coordinates contextMenu -> exec(QCursor::pos() ); delete contextMenu; } /** * @brief MainWindow::slotEditClickOnEmptySpace * Called from GW when the user clicks on empty space. */ void MainWindow::slotEditClickOnEmptySpace(const QPointF &p) { rightPanelClickedNodeLCD->display (0); rightPanelClickedNodeInDegreeLCD->display (0); rightPanelClickedNodeOutDegreeLCD->display (0); rightPanelClickedNodeClucofLCD->display(0); activeGraph.vertexClickedSet(0); activeGraph.edgeClickedSet(0,0); statusMessage( tr("Position (%1,%2): Nothing here. Cleared any selection. Double-click to create a new node." ) .arg(p.x()) .arg(p.y()) ); } /** * @brief MainWindow::slotEditNodeSelectAll */ void MainWindow::slotEditNodeSelectAll(){ qDebug() << "MainWindow::slotEditNodeSelectAll()"; graphicsWidget->selectAll(); statusMessage( tr("Selected nodes: %1") .arg( activeGraph.graphSelectedVerticesCount() ) ); } /** * @brief MainWindow::slotEditNodeSelectNone */ void MainWindow::slotEditNodeSelectNone(){ qDebug() << "MainWindow::slotEditNodeSelectNone()"; graphicsWidget->selectNone(); statusMessage( QString(tr("Selection cleared") ) ); } /** * @brief MainWindow::slotEditNodePosition * Called from GraphicsWidget when a node moves to update vertex coordinates * in Graph * @param nodeNumber * @param x * @param y */ void MainWindow::slotEditNodePosition(const int &nodeNumber, const int &x, const int &y){ qDebug("MW::slotEditNodePosition() for %i with x %i and y %i", nodeNumber, x, y); activeGraph.vertexPosSet(nodeNumber, x, y); if (!activeGraph.graphSaved()) { networkSave->setIcon(QIcon(":/images/save.png")); networkSave->setEnabled(true); } } /** * @brief MainWindow::slotEditNodeAdd * Calls Graph::vertexCreate method to add a new RANDOM node into the activeGraph. * Called when "Add Node" button is clicked on the Main Window. */ void MainWindow::slotEditNodeAdd() { qDebug() << "MW::slotEditNodeAdd() - calling Graph::vertexCreateAtPosRandom "; activeGraph.vertexCreateAtPosRandom(true); statusMessage( tr("New random positioned node (numbered %1) added.") .arg(activeGraph.vertexNumberMax()) ); } /** * @brief MainWindow::slotEditNodeAddWithMouse * Called by GW when user double-clicks at p to add a new node * Calls Graph::vertexCreateAtPos() method to add the new vertex * @param p */ void MainWindow::slotEditNodeAddWithMouse( const QPointF &p) { qDebug()<< "MW::slotEditNodeAddWithMouse() - " "Calling activeGraph::vertexCreateAtPos()"; activeGraph.vertexCreateAtPos(p); statusMessage( tr("New node (numbered %1) added at position (%2,%3)") .arg(activeGraph.vertexNumberMax()) .arg( p.x() ) .arg( p.y() ) ) ; } /** * @brief MainWindow::slotEditNodeFind * Calls GW::setMarkedNode() to find a node by its number or label. * The node is then marked. */ void MainWindow::slotEditNodeFind(){ qDebug ("MW: slotEditNodeFind()"); if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } if ( markedNodesExist ) { // if a node has been already marked graphicsWidget->setMarkedNode(""); // call setMarkedNode to just unmark it. markedNodesExist=false; statusMessage( tr("Node unmarked.") ); return; // and return to MW } bool ok=false; QString nodeText = QInputDialog::getText(this, tr("Find Node"), tr("Enter node label or node number:"), QLineEdit::Normal,QString::null, &ok ); if (!ok) { statusMessage( tr("Find node operation cancelled.") ); return; } else { if ( graphicsWidget->setMarkedNode(nodeText) ) { markedNodesExist=true; statusMessage( tr("Node found and marked. Press Ctrl+F again to unmark...") ); } else { QMessageBox::information(this, tr("Find Node"), tr("Sorry. There is no such node in this network. \n Try again."), "OK",0); } } } /** * @brief MainWindow::slotEditNodeRemove * Deletes a node and the attached objects (edges, etc). * If user has clicked on a node (signaled from GW or set by another function) * it deletes it * Else it asks for a nodeNumber to remove. The nodeNumber is doomedJim. * Called from nodeContextMenu */ void MainWindow::slotEditNodeRemove() { qDebug() << "MW::slotEditNodeRemove()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } if (activeGraph.relations() > 1){ QMessageBox::critical( this, "Error", tr("Cannot remove node! \n" "This a network with more than 1 relations. If you remove " "a node from the active relation, and then ask me to go " "to the previous or the next relation, then I would crash " "because I would try to display edges from a deleted node." "You cannot remove nodes in multirelational networks."), "OK",0); statusMessage( tr("Nothing to remove.") ); return; } // if there are already multiple nodes selected, erase them int nodesSelected = activeGraph.graphSelectedVerticesCount(); if ( nodesSelected > 0) { int removeCounter = 0; qDebug() << "MW::removeNode() multiple selected to remove"; foreach (int nodeNumber, activeGraph.graphSelectedVertices() ) { activeGraph.vertexRemove(nodeNumber); ++removeCounter ; } editNodeRemoveAct->setText(tr("Remove Node")); statusMessage( tr("Removed ") + nodesSelected + tr(" nodes. Ready. ") ); } else { int doomedJim=-1, min=-1, max=-1; bool ok=false; min = activeGraph.vertexNumberMin(); max = activeGraph.vertexNumberMax(); qDebug("MW: min is %i and max is %i", min, max); if (min==-1 || max==-1 ) { qDebug("ERROR in finding min max nodeNumbers. Abort"); return; } else { doomedJim = QInputDialog::getInt( this, tr("Remove node"), tr("Choose a node to remove between (" + QString::number(min).toLatin1()+"..."+ QString::number(max).toLatin1()+"):"),min, 1, max, 1, &ok); if (!ok) { statusMessage( "Remove node operation cancelled." ); return; } } qDebug ("MW: removing vertex with number %i from Graph", doomedJim); activeGraph.vertexRemove(doomedJim); qDebug("MW: removeNode() completed. Node %i removed completely.",doomedJim); statusMessage( tr("Node removed completely. Ready. ") ); } } /** * @brief MainWindow::slotEditNodePropertiesDialog * Reads values from selected nodes * then open Node Properties dialog */ void MainWindow::slotEditNodePropertiesDialog() { qDebug() << "MW::slotEditNodePropertiesDialog()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int min=-1, max=-1, size = appSettings["initNodeSize"].toInt(0, 10); int nodeNumber = 0; int selectedNodesCount = activeGraph.graphSelectedVerticesCount(); QColor color = QColor(appSettings["initNodeColor"]); QString shape= appSettings["initNodeShape"]; QString label=""; bool ok=false; if ( selectedNodesCount == 0) { min = activeGraph.vertexNumberMin(); max = activeGraph.vertexNumberMax(); qDebug() << "MW::slotEditNodePropertiesDialog() - no node selected" << "min node number " << min << "max node number " << max << "opening inputdialog"; if (min==-1 || max==-1 ) { qDebug("ERROR in finding min max nodeNumbers. Abort"); return; } nodeNumber = QInputDialog::getInt( this, "Node Properties", tr("Choose a node between (" + QString::number(min).toLatin1() +"..." + QString::number(max).toLatin1()+"):"),min, 1, max, 1, &ok); if (!ok) { statusMessage( "Node properties cancelled." ); return; } } else { foreach (nodeNumber, activeGraph.graphSelectedVertices() ) { qDebug() << "MW::slotEditNodePropertiesDialog() " "changing properties for selected node " << nodeNumber ; if ( selectedNodesCount > 1 ) { color = activeGraph.vertexColor( nodeNumber ); shape = activeGraph.vertexShape( nodeNumber); size = activeGraph.vertexSize ( nodeNumber); } else { label = activeGraph.vertexLabel( nodeNumber ); color = activeGraph.vertexColor( nodeNumber ); shape = activeGraph.vertexShape( nodeNumber); size = activeGraph.vertexSize ( nodeNumber); } } } //@todo add some grouping function here? m_nodeEditDialog = new DialogNodeEdit(this, label, size, color, shape) ; connect( m_nodeEditDialog, &DialogNodeEdit::userChoices, this, &MainWindow::slotEditNodeProperties ); m_nodeEditDialog->exec(); statusMessage( tr("Node properties dialog opened. Ready. ") ); } /** * @brief MainWindow::slotEditNodeProperties * Applies new (user-defined) values to all selected nodes * Called on exit from DialogNodeEdit * @param label * @param size * @param value * @param color * @param shape */ void MainWindow::slotEditNodeProperties( const QString label, const int size, const QString value, const QColor color, const QString shape) { int selectedNodesCount = activeGraph.graphSelectedVerticesCount(); qDebug()<< "MW::slotEditNodeProperties() - new properties: " << " label " << label << " size " << size << "value " << value << " color " << color << " shape " << shape << " vertexClicked " < 1 ) { activeGraph.vertexLabelSet( nodeNumber, label + QString::number(nodeNumber) ); } else activeGraph.vertexLabelSet( nodeNumber, label ); if ( label !="" && appSettings["initNodeLabelsVisibility"] != "true") slotOptionsNodeLabelsVisibility(true); qDebug()<< "MW::slotEditNodeProperties() - updating color "; activeGraph.vertexColorSet( nodeNumber, color.name()); qDebug()<< "MW::slotEditNodeProperties() - updating size "; activeGraph.vertexSizeSet(nodeNumber,size); qDebug()<< "MW::slotEditNodeProperties() - updating shape "; activeGraph.vertexShapeSet( nodeNumber, shape); } } statusMessage( tr("Ready. ")); } /** * @brief Creates a complete subgraph (clique) from selected nodes. * Calls Graph::verticesSelectedCreateClique() */ void MainWindow::slotEditNodeSelectedToClique () { qDebug() << "MW::slotEditNodeSelectedToClique()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int selectedNodesCount = activeGraph.graphSelectedVerticesCount(); if ( selectedNodesCount == 0 ) { slotHelpMessageToUser(USER_MSG_INFO,tr("No nodes selected."), tr("Cannot create new clique. No nodes are selected."), tr("Select some nodes first.") ); return; } activeGraph.verticesCreateSubgraph(QList (), SUBGRAPH_CLIQUE); slotHelpMessageToUser(USER_MSG_INFO,tr("Clique created."), tr("A new clique has been created from ") + QString::number(selectedNodesCount) + tr(" nodes") ); } /** * @brief Creates a star subgraph from selected nodes. * User must choose a central actor. * Calls Graph::slotEditNodeSelectedToStar() */ void MainWindow::slotEditNodeSelectedToStar() { qDebug() << "MW::slotEditNodeSelectedToStar()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int selectedNodesCount = activeGraph.graphSelectedVerticesCount(); if ( selectedNodesCount == 0 ) { slotHelpMessageToUser(USER_MSG_INFO,tr("No nodes selected."), tr("Cannot create star subgraph. No nodes are selected."), tr("Select some nodes first.") ); return; } int center; bool ok=false; int min = activeGraph.graphSelectedVerticesMin(); int max = activeGraph.graphSelectedVerticesMax(); center=QInputDialog::getInt( this, "Create star subgraph", tr("To create a star subgraph from selected nodes, \n" "enter the number of the central actor (" +QString::number(min).toLatin1()+"..." +QString::number(max).toLatin1()+"):"), min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Create star subgraph cancelled." ); return; } activeGraph.verticesCreateSubgraph(QList (), SUBGRAPH_STAR,center); slotHelpMessageToUser(USER_MSG_INFO,tr("Star subgraph created."), tr("A new star subgraph has been created from ") + QString::number( selectedNodesCount ) + tr(" nodes") ); } /** * @brief Creates a cycle subgraph from selected nodes. * Calls Graph::verticesSelectedCreateCycle() */ void MainWindow::slotEditNodeSelectedToCycle() { qDebug() << "MW::slotEditNodeSelectedToCycle()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int selectedNodesCount = activeGraph.graphSelectedVerticesCount(); if ( selectedNodesCount == 0 ) { slotHelpMessageToUser(USER_MSG_INFO,tr("No nodes selected."), tr("Cannot create cycle subgraph. No nodes are selected."), tr("Select some nodes first.") ); return; } activeGraph.verticesCreateSubgraph(QList (),SUBGRAPH_CYCLE); slotHelpMessageToUser(USER_MSG_INFO,tr("Cycle subgraph created."), tr("A new cycle subgraph has been created from ") + QString::number( selectedNodesCount ) + tr(" nodes") ); } /** * @brief Creates a line subgraph from selected nodes. * Calls Graph::verticesSelectedCreateLine() */ void MainWindow::slotEditNodeSelectedToLine() { qDebug() << "MW::slotEditNodeSelectedToLine()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int selectedNodesCount = activeGraph.graphSelectedVerticesCount(); if ( selectedNodesCount == 0 ) { slotHelpMessageToUser(USER_MSG_INFO,tr("No nodes selected."), tr("Cannot create line subgraph. No nodes are selected."), tr("Select some nodes first.") ); return; } activeGraph.verticesCreateSubgraph(QList (),SUBGRAPH_LINE); slotHelpMessageToUser(USER_MSG_INFO,tr("Line subgraph created."), tr("A new line subgraph has been created from ") + QString::number( selectedNodesCount ) + tr(" nodes") ); } /** * @brief MainWindow::slotEditNodeColorAll * Changes the color of all nodes to parameter color * Calls activeGraph.vertexColorAllSet to do the work * If parameter color is invalid, opens a QColorDialog to * select a new node color for all nodes. * Called from Settings Dialog and Edit menu option * @param color */ void MainWindow::slotEditNodeColorAll(QColor color){ if (!color.isValid()) { color = QColorDialog::getColor( QColor ( appSettings["initNodeColor"] ), this, "Change the color of all nodes" ); } if (color.isValid()) { appSettings["initNodeColor"] = color.name(); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); qDebug() << "MW::slotEditNodeColorAll() : " << appSettings["initNodeColor"]; activeGraph.vertexColorAllSet(appSettings["initNodeColor"]); QApplication::restoreOverrideCursor(); statusMessage( tr("Ready. ") ); } else { // user pressed Cancel statusMessage( tr("Invalid color. ") ); } } /** * @brief MainWindow::slotEditNodeSizeAll * Changes the size of nodes to newSize. * Calls activeGraph.vertexSizeAllSet to do the work. * Called from Edit menu item, DialogSettings * If newSize = 0 asks the user a new size for all nodes * If normalized = true, changes node sizes according to their plethos * @param newSize * @param normalized */ void MainWindow::slotEditNodeSizeAll(int newSize, const bool &normalized) { Q_UNUSED(normalized); qDebug () << "MW: slotEditNodeSizeAll() - " << " newSize " << newSize ; if ( newSize == 0 && !normalized ) { bool ok=true; newSize = QInputDialog::getInt( this, "Change node size", tr("Select new size for all nodes:"), appSettings["initNodeSize"].toInt(0, 10), 1, 100, 1, &ok ); if (!ok) { statusMessage( "Change node size operation cancelled." ); return; } } appSettings["initNodeSize"]= QString::number(newSize); nodeSizesByOutDegreeAct->setChecked(false); toolBoxNodeSizesByOutDegreeBx->setChecked(false); nodeSizesByInDegreeAct->setChecked(false); toolBoxNodeSizesByInDegreeBx->setChecked(false); activeGraph.vertexSizeAllSet(newSize); statusMessage(tr("Ready")); return; } /** * @brief MainWindow::slotEditNodeShape * If shape == null, prompts the user a list of available node shapes to select. * Then changes the shape of all nodes/vertices accordingly. * If vertex is non-zero, changes the shape of that node only. * Called when user clicks on Edit -> Node > Change all nodes shapes * Called from DialogSettings when the user has selected a new default node shape * Calls Graph::vertexShapeAllSet(QString) * @param shape * @param vertex */ void MainWindow::slotEditNodeShape(QString shape, const int vertex) { qDebug() << "MW::slotEditNodeShape() - vertex " << vertex << " (0 means all) - new shape " << shape; if (shape==QString::null) { bool ok=false; QStringList lst; lst << "box"<< "circle"<< "diamond" << "ellipse"<< "triangle" << "star"; int curShapeIndex = lst.indexOf(appSettings["initNodeShape"]); if ( curShapeIndex == -1 ) { curShapeIndex=1; } shape = QInputDialog::getItem(this, "Node shape", "Select a shape for all nodes: ", lst, curShapeIndex, true, &ok); if ( !ok ) { //user pressed Cancel statusMessage(tr("Change node shapes aborted.")); return; } } if (vertex == 0) { //change all nodes shapes activeGraph.vertexShapeAllSet(shape); appSettings["initNodeShape"] = shape; statusMessage(tr("All shapes have been changed. Ready.")); } else { //only one activeGraph.vertexShapeSet( vertex, shape); statusMessage(tr("Node shape has been changed. Ready.")); } } /** * @brief MainWindow::slotEditNodeNumberSize * Changes the size of one or all node numbers. * Called from Edit menu option and DialogSettings * if newSize=0, asks the user to enter a new node number font size * if v1=0, it changes all node numbers * @param v1 * @param newSize */ void MainWindow::slotEditNodeNumberSize(int v1, int newSize, const bool prompt) { bool ok=false; qDebug() << "MW::slotEditNodeNumberSize - newSize " << newSize; if (prompt) { newSize = QInputDialog::getInt(this, "Change text size", tr("Change all node numbers size to: (1-16)"), appSettings["initNodeNumberSize"].toInt(0,10), 1, 16, 1, &ok ); if (!ok) { statusMessage( tr("Change font size: Aborted.") ); return; } } if (v1) { //change one node number only activeGraph.vertexNumberSizeSet(v1, newSize); } else { //change all appSettings["initNodeNumberSize"] = QString::number(newSize); activeGraph.vertexNumberSizeSetAll(newSize); } statusMessage( tr("Changed node numbers size. Ready.") ); } /** * @brief MainWindow::slotEditNodeNumbersColor * Changes the color of all nodes' numbers. * Called from Edit menu option and Settings dialog. * Asks the user to enter a new node number color */ void MainWindow::slotEditNodeNumbersColor(QColor color){ qDebug() << "MW:slotEditNodeNumbersColor() - new color " << color; if (!color.isValid()) { color = QColorDialog::getColor( QColor ( appSettings["initNodeNumberColor"] ), this, "Change the color of all node numbers" ); } if (color.isValid()) { QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); QList list= scene->items(); for (QList::iterator it=list.begin(); it!=list.end(); it++) { if ( (*it)->type() == TypeNumber) { NodeNumber *jimNumber = (NodeNumber *) (*it); jimNumber->update(); jimNumber->setDefaultTextColor(color); } } appSettings["initNodeNumberColor"] = color.name(); activeGraph.vertexNumberColorInit( color.name() ); QApplication::restoreOverrideCursor(); statusMessage( tr("Numbers' colors changed. Ready. ") ); } else { // user pressed Cancel statusMessage( tr("Invalid color. ") ); } } /** * @brief MainWindow::slotEditNodeNumberDistance * Changes the distance of one or all node numbers from their nodes. * Called from Edit menu option and DialogSettings * if newDistance=0, asks the user to enter a new node number distance * if v1=0, it changes all node number distances * @param v1 * @param newDistance */ void MainWindow::slotEditNodeNumberDistance(int v1, int newDistance) { bool ok=false; qDebug() << "MW::slotEditNodeNumberDistance - newSize " << newDistance; if (!newDistance) { newDistance = QInputDialog::getInt( this, "Change node number distance", tr("Change all node numbers distance from their nodes to: (1-16)"), appSettings["initNodeNumberDistance"].toInt(0,10), 1, 16, 1, &ok ); if (!ok) { statusMessage( tr("Change node number distance aborted.") ); return; } } if (v1) { //change one node number distance only activeGraph.vertexNumberDistanceSet(v1, newDistance); } else { //change all appSettings["initNodeNumberDistance"] = QString::number(newDistance); activeGraph.vertexNumberDistanceSetAll(newDistance); } statusMessage( tr("Changed node number distance. Ready.") ); } /** * @brief MainWindow::slotEditNodeLabelSize * Changes the size of one or all node Labels. * Called from Edit menu option and DialogSettings * if newSize=0, asks the user to enter a new node Label font size * if v1=0, it changes all node Labels * @param v1 * @param newSize */ void MainWindow::slotEditNodeLabelSize(int v1, int newSize) { bool ok=false; qDebug() << "MW::slotEditNodeLabelSize - newSize " << newSize; if (!newSize) { newSize = QInputDialog::getInt(this, "Change text size", tr("Change all node labels text size to: (1-16)"), appSettings["initNodeLabelSize"].toInt(0,10), 1, 32, 1, &ok ); if (!ok) { statusMessage( tr("Change font size: Aborted.") ); return; } } if (v1) { //change one node Label only activeGraph.vertexLabelSizeSet(v1, newSize); } else { //change all appSettings["initNodeLabelSize"] = QString::number(newSize); activeGraph.vertexLabelSizeAllSet(newSize); } statusMessage( tr("Changed node label size. Ready.") ); } /** * @brief MainWindow::slotEditNodeLabelsColor * Changes the color of all nodes' labels. * Asks the user to enter a new node label color */ void MainWindow::slotEditNodeLabelsColor(QColor color){ qDebug() << "MW:slotEditNodeNumbersColor() - new color " << color; if (!color.isValid()) { color = QColorDialog::getColor( QColor ( appSettings["initNodeLabelColor"] ), this, "Change the color of all node labels" ); } if (color.isValid()) { QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); activeGraph.vertexLabelColorAllSet(color.name()); appSettings["initNodeLabelColor"] = color.name(); optionsNodeLabelsVisibilityAct->setChecked(true); QApplication::restoreOverrideCursor(); statusMessage( tr("Label colors changed. Ready. ") ); } else { // user pressed Cancel statusMessage( tr("Invalid color. ") ); } } /** * @brief MainWindow::slotEditNodeLabelDistance * Changes the distance of one or all node label from their nodes. * Called from Edit menu option and DialogSettings * if newDistance=0, asks the user to enter a new node label distance * if v1=0, it changes all node label distances * @param v1 * @param newDistance */ void MainWindow::slotEditNodeLabelDistance(int v1, int newDistance) { bool ok=false; qDebug() << "MW::slotEditNodeLabelDistance - newSize " << newDistance; if (!newDistance) { newDistance = QInputDialog::getInt( this, "Change node label distance", tr("Change all node labels distance from their nodes to: (1-16)"), appSettings["initNodeLabelDistance"].toInt(0,10), 1, 16, 1, &ok ); if (!ok) { statusMessage( tr("Change node label distance aborted.") ); return; } } if (v1) { //change one node label distance only activeGraph.vertexLabelDistanceSet(v1, newDistance); } else { //change all appSettings["initNodeLabelDistance"] = QString::number(newDistance); activeGraph.vertexLabelDistanceAllSet(newDistance); } statusMessage( tr("Changed node label distance. Ready.") ); } /** * @brief MainWindow::slotEditNodeOpenContextMenu * Called from GW when the user has right-clicked on a node * Opens a node context menu with some options when the user right-clicks on a node */ void MainWindow::slotEditNodeOpenContextMenu() { qDebug("MW: slotEditNodeOpenContextMenu() for node %i at %i, %i", activeGraph.vertexClicked(), QCursor::pos().x(), QCursor::pos().y()); QMenu *nodeContextMenu = new QMenu(QString::number( activeGraph.vertexClicked() ), this); Q_CHECK_PTR( nodeContextMenu ); //displays "out of memory" if needed int nodesSelected = activeGraph.graphSelectedVerticesCount(); if ( nodesSelected == 1) { nodeContextMenu -> addAction( tr("## NODE ") + QString::number(activeGraph.vertexClicked()) + " ## " ); } else { nodeContextMenu -> addAction( tr("## NODE ") + QString::number(activeGraph.vertexClicked()) + " ## " + tr(" (selected nodes: ") + QString::number ( nodesSelected ) + ")"); } nodeContextMenu -> addSeparator(); nodeContextMenu -> addAction(editNodePropertiesAct ); nodeContextMenu -> addSeparator(); nodeContextMenu -> addAction(editEdgeAddAct); nodeContextMenu -> addSeparator(); nodeContextMenu -> addAction(editNodeRemoveAct ); nodeContextMenu -> addSeparator(); //QCursor::pos() is good only for menus not related with node coordinates nodeContextMenu -> exec(QCursor::pos() ); delete nodeContextMenu; } /** * @brief MainWindow::slotEditSelectionChanged * @param nodes * @param edges */ void MainWindow::slotEditSelectionChanged(const int &selNodes, const int &selEdges) { qDebug()<< "MW::slotEditSelectionChanged()"; rightPanelSelectedNodesLCD->display(selNodes); rightPanelSelectedEdgesLCD->display(selEdges); if (selNodes > 1 ){ editNodeRemoveAct->setText(tr("Remove ") + QString::number(selNodes) + tr(" nodes")); editNodeSelectedToCliqueAct->setEnabled(true); editNodeSelectedToCliqueAct->setText(tr("Create a clique from ") + QString::number(selNodes) + tr(" selected nodes")); editNodeSelectedToStarAct->setEnabled(true); editNodeSelectedToStarAct->setText(tr("Create a star from ") + QString::number(selNodes) + tr(" selected nodes")); editNodeSelectedToCycleAct->setEnabled(true); editNodeSelectedToCycleAct->setText(tr("Create a cycle from ") + QString::number(selNodes) + tr(" selected nodes")); editNodeSelectedToLineAct->setEnabled(true); editNodeSelectedToLineAct->setText(tr("Create a line from ") + QString::number(selNodes) + tr(" selected nodes")); } else { editNodeRemoveAct->setText(tr("Remove Node")); editNodeSelectedToCliqueAct->setText(tr("Create a clique from selected nodes")); editNodeSelectedToCliqueAct->setEnabled(false); editNodeSelectedToStarAct->setText(tr("Create a star from selected nodes")); editNodeSelectedToStarAct->setEnabled(false); editNodeSelectedToCycleAct->setText(tr("Create a cycle from selected nodes")); editNodeSelectedToCycleAct->setEnabled(false); editNodeSelectedToLineAct->setText(tr("Create a line from selected nodes")); editNodeSelectedToLineAct->setEnabled(false); } statusMessage( tr("Selected %1 nodes and %2 edges") .arg( selNodes ) .arg( selEdges ) ); } /** * @brief MainWindow::slotEditNodeInfoStatusBar * Called by Graph::userClickedNode() signal, when the user clicks on a node. * It displays information about the node on the statusbar. * @param jim */ void MainWindow::slotEditNodeInfoStatusBar (const int &number, const QPointF &p, const QString &label, const int &inDegree, const int &outDegree, const float &clc) { qDebug()<<"MW::slotEditNodeInfoStatusBar()"; rightPanelClickedNodeLCD->display (number); rightPanelClickedNodeInDegreeLCD->display (inDegree); rightPanelClickedNodeOutDegreeLCD->display (outDegree); rightPanelClickedNodeClucofLCD->display(clc); if (number!=0) { statusMessage( QString(tr("Position (%1, %2): Node %3, label %4 - " "In-Degree: %5, Out-Degree: %6")) .arg( ceil( p.x() ) ) .arg( ceil( p.y() )).arg( number ) .arg( ( label == "") ? "unset" : label ) .arg(inDegree).arg(outDegree) ); } } /** * @brief MainWindow::slotEditEdgeInfoStatusBar * Called by GW::selectedEdge signal when the user clickes on an edge * Displays information about the clicked edge on the statusbar * @param edge */ void MainWindow::slotEditEdgeInfoStatusBar (const int &v1, const int &v2, const float &weight, const bool &undirected) { rightPanelClickedEdgeSourceLCD->display(v1); rightPanelClickedEdgeTargetLCD->display(v2); rightPanelClickedEdgeWeightLCD->display(weight); if (v1 ==0 || v2 == 0) return; if ( undirected ) { statusMessage( QString (tr("Symmetric edge %1 <--> %2 of weight %3 has been selected. " "Click anywhere else to unselect it.")) .arg( v1 ).arg( v2 ) .arg( weight ) ); rightPanelClickedEdgeHeaderLabel->setText(tr("Clicked Edge")); } else { statusMessage( QString(tr("Arc %1 --> %2 of weight %3 has been selected. " "Click again to unselect it.")) .arg( v1 ).arg( v2 ) .arg( weight ) ); rightPanelClickedEdgeHeaderLabel->setText(tr("Clicked Directed Edge")); } } /** * @brief MainWindow::slotEditEdgeOpenContextMenu * Called by GW::openEdgeMenu when the user right-clicks on an edge * Popups a context menu with edge- related options */ void MainWindow::slotEditEdgeOpenContextMenu() { int source=activeGraph.edgeClicked().v1; int target=activeGraph.edgeClicked().v2; qDebug("MW: slotEditEdgeOpenContextMenu() for edge %i-%i at %i, %i",source, target, QCursor::pos().x(), QCursor::pos().y()); QString edgeName=QString::number(source)+QString("->")+QString::number(target); //make the menu QMenu *edgeContextMenu = new QMenu(edgeName, this); edgeContextMenu -> addAction( "## EDGE " + edgeName + " ## "); edgeContextMenu -> addSeparator(); edgeContextMenu -> addAction( editEdgeRemoveAct ); edgeContextMenu -> addAction( editEdgeWeightAct ); edgeContextMenu -> addAction( editEdgeLabelAct ); edgeContextMenu -> addAction( editEdgeColorAct ); edgeContextMenu -> exec(QCursor::pos() ); delete edgeContextMenu; } /** * @brief MainWindow::slotEditEdgeAdd * Adds a new edge between two nodes specified by the user. * Called when user clicks on the MW button/menu item "Add edge" */ void MainWindow::slotEditEdgeAdd(){ qDebug ()<<"MW::slotEditEdgeAdd()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int sourceNode=-1, targetNode=-1; float weight=1; //weight of this new edge should be one... bool ok=false; int min=activeGraph.vertexNumberMin(); int max=activeGraph.vertexNumberMax(); if (min==max) return; //if there is only one node -> no edge if ( ! activeGraph.vertexClicked() ) { sourceNode=QInputDialog::getInt( this, "Create new edge, Step 1", tr("This will draw a new edge between two nodes. \n" "Enter source node (" +QString::number(min).toLatin1()+"..." +QString::number(max).toLatin1()+"):"), min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Add edge operation cancelled." ); return; } } else sourceNode=activeGraph.vertexClicked(); qDebug ()<<"MW::slotEditEdgeAdd() - sourceNode:" << sourceNode; if ( activeGraph.vertexExists(sourceNode) ==-1 ) { statusMessage( tr("Aborting. ") ); QMessageBox::critical(this,"Error","No such node.", "OK",0); qDebug ()<<"MW::slotEditEdgeAdd() - cannot find sourceNode:" << sourceNode; return; } targetNode=QInputDialog::getInt (this, "Create new edge, Step 2", tr( "Source node:" ) + QString::number( sourceNode ) + tr(" \nNow enter a target node [") + QString::number(min).toLatin1() + "..." + QString::number(max).toLatin1()+"]:",min, min, max , 1, &ok) ; if (!ok) { statusMessage( "Add edge target operation cancelled." ); return; } if ( activeGraph.vertexExists(targetNode) ==-1 ) { statusMessage( tr("Aborting. ") ); QMessageBox::critical(this,"Error","No such node.", "OK",0); qDebug ("MW: slotEditEdgeAdd: Cant find targetNode %i",targetNode); return; } weight=QInputDialog::getDouble( this, "Create new edge, Step 3", tr("Source and target nodes accepted. \n" "Please, enter the weight of new edge: "),1.0, -100.0, 100.0, 1, &ok); if (!ok) { statusMessage( "Add edge operation cancelled." ); return; } //Check if this edge already exists... if (activeGraph.edgeExists(sourceNode, targetNode)!=0 ) { qDebug("edge exists. Aborting"); statusMessage( tr("Aborting. ") ); QMessageBox::critical(this,"Error","edge already exists.", "OK",0); return; } slotEditEdgeCreate(sourceNode, targetNode, weight); statusMessage( tr("Ready. ") ); } /** * @brief MainWindow::slotEditEdgeCreate * helper to slotEditEdgeAdd() above * Also called from GW::userMiddleClicked() signal when user creates edges with middle-clicks * Calls Graph::edgeCreate method to add the new edge to the active Graph * @param source * @param target * @param weight */ void MainWindow::slotEditEdgeCreate (const int &source, const int &target, const float &weight) { qDebug()<< "MW::slotEditEdgeCreate() - edge" << source << "->" << target << "weight" << weight << "Setting user settings and calling Graph::edgeCreate(...)"; //int reciprocal=0; bool bezier = false; activeGraph.edgeCreate( source, target, weight, appSettings["initEdgeColor"] , ( editEdgeUndirectedAllAct->isChecked() ) ? 2:0, ( editEdgeUndirectedAllAct->isChecked() ) ? false : ( (appSettings["initEdgeArrows"] == "true") ? true: false) , bezier); if ( activeEdges() == 1 && editRelationChangeCombo->count() == 0 ) { slotEditRelationAdd(); } } /** * @brief MainWindow::slotEditEdgeRemove * Erases the clicked edge. Otherwise asks the user to specify one edge. * First deletes arc reference from object nodeVector then deletes arc item from scene */ void MainWindow::slotEditEdgeRemove(){ if ( !activeNodes() || activeEdges() ==0 ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_EDGES); return; } int min=0, max=0, sourceNode=-1, targetNode=-1; bool ok=false; min=activeGraph.vertexNumberMin(); max=activeGraph.vertexNumberMax(); if (!activeGraph.edgeClicked().v1 || !activeGraph.edgeClicked().v2 ) { sourceNode=QInputDialog::getInt( this,tr("Remove edge"), tr("Source node: (")+QString::number(min)+ "..."+QString::number(max)+"):", min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Remove edge operation cancelled." ); return; } targetNode=QInputDialog::getInt( this, tr("Remove edge"), tr("Target node: (")+QString::number(min)+"..."+ QString::number(max)+"):",min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Remove edge operation cancelled." ); return; } if ( activeGraph.edgeExists(sourceNode, targetNode)!=0 ) { activeGraph.edgeRemove(sourceNode, targetNode); } else { QMessageBox::critical( this, "Remove edge",tr("There is no such edge."), "OK",0); statusMessage( tr("There are no nodes yet...") ); return; } } else { sourceNode = activeGraph.edgeClicked().v1; targetNode = activeGraph.edgeClicked().v2; activeGraph.edgeRemove(sourceNode, targetNode); } qDebug()<< "MW::slotEditEdgeRemove() -" << "View items now:"<< graphicsWidget->items().size() << "Scene items now:"<< scene->items().size(); } /** * @brief MainWindow::slotEditEdgeLabel */ void MainWindow::slotEditEdgeLabel(){ qDebug() << "MW::slotEditEdgeLabel()"; if ( !activeEdges() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_EDGES); return; } int sourceNode=-1, targetNode=-1; bool ok=false; int min=activeGraph.vertexNumberMin(); int max=activeGraph.vertexNumberMax(); if (!activeGraph.edgeClicked().v1 || !activeGraph.edgeClicked().v2 ) { //no edge clicked. Ask user to define an edge. sourceNode=QInputDialog::getInt(this, "Change edge label", tr("Select edge source node: ("+ QString::number(min).toLatin1()+ "..."+QString::number(max).toLatin1()+ "):"), min, 1, max , 1, &ok) ; if (!ok) { statusMessage( "Change edge label operation cancelled." ); return; } targetNode=QInputDialog::getInt(this, "Change edge label...", tr("Select edge target node: ("+ QString::number(min).toLatin1()+"..." + QString::number(max).toLatin1()+"):"), min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Change edge label operation cancelled." ); return; } if ( ! activeGraph.edgeExists (sourceNode, targetNode ) ) { statusMessage( tr("There is no such edge. ") ); QMessageBox::critical(this, "Error", tr("No edge! \nNo such edge found in current network."), "OK",0); return; } } else { //edge has been clicked. sourceNode = activeGraph.edgeClicked().v1; targetNode = activeGraph.edgeClicked().v2; } QString label = QInputDialog::getText( this, tr("Change edge label"), tr("Enter label: ") ); if ( !label.isEmpty()) { qDebug() << "MW::slotEditEdgeLabel() - " << sourceNode << " -> " << targetNode << " new label " << label; activeGraph.edgeLabelSet( sourceNode, targetNode, label); slotOptionsEdgeLabelsVisibility(true); statusMessage( tr("Ready. ") ); } else { statusMessage( tr("Change edge label aborted. ") ); } } /** * @brief MainWindow::slotEditEdgeColorAll * It changes the color of all edges weighted below threshold to parameter color * If color is not valid, it opens a QColorDialog * If threshold == RAND_MAX it changes the color of all edges. * Called from Edit -> Edges menu option and Settings Dialog. * @param color = QColor() * @param threshold = RAND_MAX */ void MainWindow::slotEditEdgeColorAll(QColor color,const int &threshold){ if (!color.isValid()) { QString text; if (threshold < RAND_MAX) { text = "Change the color of edges weighted < " + QString::number(threshold) ; } else text = "Change the color of all edges" ; color = QColorDialog::getColor( appSettings["initEdgeColor"], this, text); } if (color.isValid()) { if (threshold < 0 ) { appSettings["initEdgeColorNegative"]=color.name(); } else appSettings["initEdgeColor"]=color.name(); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); qDebug() << "MainWindow::slotEditEdgeColorAll() - new edge color: " << color.name(); activeGraph.edgeColorAllSet(color.name(), threshold ); QApplication::restoreOverrideCursor(); statusMessage( tr("Ready. ") ); } else { // user pressed Cancel statusMessage( tr("edges color change aborted. ") ); } } /** * @brief MainWindow::slotEditEdgeColor * Changes the color of the clicked edge. * If no edge is clicked, then it asks the user to specify one. */ void MainWindow::slotEditEdgeColor(){ qDebug() << "MW::slotEditEdgeColor()"; if ( !activeEdges() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_EDGES); return; } int sourceNode=-1, targetNode=-1; bool ok=false; int min=activeGraph.vertexNumberMin(); int max=activeGraph.vertexNumberMax(); if (!activeGraph.edgeClicked().v1 || !activeGraph.edgeClicked().v2) { //no edge clicked. Ask user to define an edge. sourceNode=QInputDialog::getInt(this, "Change edge color", tr("Select edge source node: ("+ QString::number(min).toLatin1()+ "..."+QString::number(max).toLatin1()+ "):"), min, 1, max , 1, &ok) ; if (!ok) { statusMessage( "Change edge color operation cancelled." ); return; } targetNode=QInputDialog::getInt(this, "Change edge color...", tr("Select edge target node: ("+ QString::number(min).toLatin1()+"..." + QString::number(max).toLatin1()+"):"), min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Change edge color operation cancelled." ); return; } if ( ! activeGraph.edgeExists(sourceNode, targetNode ) ) { statusMessage( tr("There is no such edge. ") ); QMessageBox::critical(this, "Error", tr("No edge! \nNo such edge found in current network."), "OK",0); return; } } else { //edge has been clicked. sourceNode = activeGraph.edgeClicked().v1; targetNode = activeGraph.edgeClicked().v2; } QString curColor = activeGraph.edgeColor(sourceNode, targetNode); if (!QColor(curColor).isValid()) { curColor=appSettings["initEdgeColor"]; } QColor color = QColorDialog::getColor( curColor, this, tr("Select new color....") ); if ( color.isValid()) { QString newColor=color.name(); qDebug() << "MW::slotEditEdgeColor() - " << sourceNode << " -> " << targetNode << " newColor " << newColor; activeGraph.edgeColorSet( sourceNode, targetNode, newColor); statusMessage( tr("Ready. ") ); } else { statusMessage( tr("Change edge color aborted. ") ); } } /** * @brief MainWindow::slotEditEdgeWeight * Changes the weight of the clicked edge. * If no edge is clicked, asks the user to specify an Edge. */ void MainWindow::slotEditEdgeWeight(){ if ( !activeEdges() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_EDGES); return; } qDebug("MW::slotEditEdgeWeight()"); int sourceNode=-1, targetNode=-1; float newWeight=1.0; int min=activeGraph.vertexNumberMin(); int max=activeGraph.vertexNumberMax(); bool ok=false; if ( activeGraph.edgeClicked().v1==0 || activeGraph.edgeClicked().v2==0 ) { sourceNode=QInputDialog::getInt( this, "Change edge weight", tr("Select edge source node: ("+ QString::number(min).toLatin1()+"..."+ QString::number(max).toLatin1()+"):"), min, 1, max , 1, &ok) ; if (!ok) { statusMessage( "Change edge weight operation cancelled." ); return; } targetNode=QInputDialog::getInt( this, "Change edge weight...", tr("Select edge target node: ("+ QString::number(min).toLatin1()+"..."+ QString::number(max).toLatin1()+"):"), min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Change edge weight operation cancelled." ); return; } qDebug("source %i target %i",sourceNode, targetNode); } else { qDebug() << "MW: slotEditEdgeWeight() - an Edge has already been clicked"; sourceNode=activeGraph.edgeClicked().v1; targetNode=activeGraph.edgeClicked().v2; qDebug() << "MW: slotEditEdgeWeight() from " << sourceNode << " to " << targetNode; } float oldWeight= 0; if ( ( oldWeight= activeGraph.edgeWeight(sourceNode, targetNode)) != 0 ) { newWeight=(float) QInputDialog::getDouble( this, "Change edge weight...", tr("New edge Weight: "), oldWeight, -100, 100 ,1, &ok ) ; if (ok) { activeGraph.edgeWeightSet(sourceNode, targetNode, newWeight, activeGraph.graphUndirected() ); } else { statusMessage( QString(tr("Change edge weight cancelled.")) ); return; } } } /** * @brief MainWindow::slotEditEdgeSymmetrizeAll * Symmetrize the ties between every two connected nodes. * If there is an arc from Node A to Node B, * then a new arc from Node B to Node is created of the same weight. * Thus, all arcs become reciprocal and the network becomes symmetric * with a symmetric adjacency matrix */ void MainWindow::slotEditEdgeSymmetrizeAll(){ if ( activeEdges() ==0 ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_EDGES); return; } qDebug("MW: slotEditEdgeSymmetrizeAll() calling graphSymmetrize()"); activeGraph.graphSymmetrize(); QMessageBox::information(this, "Symmetrize", tr("All arcs are reciprocal. \n" "The network is symmetric."), "OK",0); statusMessage(tr("All arcs are now reciprocal. Thus a symmetric network. Ready.")); } /** * @brief Adds a new symmetric relation with ties only between pairs of nodes * who are cocited by others. */ void MainWindow::slotEditEdgeSymmetrizeCocitation(){ if ( activeEdges() ==0 ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_EDGES); return; } qDebug("MW: slotEditEdgeSymmetrizeCocitation() calling graphCocitation()"); activeGraph.graphCocitation(); slotHelpMessageToUser(USER_MSG_INFO,tr("New symmetric cocitation relation created."), tr("New cocitation relation created from strong ties"), tr("A new relation \"%1\" has been added to the network. " "by counting cocitation ties only. " "This relation is symmetric. ").arg("Cocitation")); } /** * @brief MainWindow::slotEditEdgeSymmetrizeStrongTies */ void MainWindow::slotEditEdgeSymmetrizeStrongTies(){ if ( activeEdges() ==0 ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_EDGES); return; } qDebug()<< "MW::slotEditEdgeSymmetrizeStrongTies() - calling graphSymmetrizeStrongTies()"; int oldRelationsCounter=activeGraph.relations(); int answer=0; if (oldRelationsCounter>0) { switch ( answer=slotHelpMessageToUser(USER_MSG_QUESTION_CUSTOM, tr("Select"), tr("Symmetrize social network by examining strong ties"), tr("This network has multiple relations. " "Symmetrize by examining reciprocated ties across all relations or just the current relation?"), QMessageBox::NoButton, QMessageBox::NoButton, tr("all relations"), tr("current relation") ) ){ case 1: activeGraph.graphSymmetrizeStrongTies(true); break; case 2: activeGraph.graphSymmetrizeStrongTies(false); break; } } else { activeGraph.graphSymmetrizeStrongTies(false); } slotHelpMessageToUser(USER_MSG_INFO,tr("New symmetric relation created from strong ties"), tr("New relation created from strong ties"), tr("A new relation \"%1\" has been added to the network. " "by counting reciprocated ties only. " "This relation is binary and symmetric. ").arg("Strong Ties")); } /** * @brief MainWindow::slotEditEdgeUndirectedAll * Tranforms all directed arcs to undirected edges. * The result is a undirected and symmetric network */ void MainWindow::slotEditEdgeUndirectedAll(const bool &toggle){ qDebug()<<"MW: slotEditEdgeUndirectedAll() - calling Graph::graphUndirectedSet()"; if (toggle) { activeGraph.graphUndirectedSet(toggle); optionsEdgeArrowsAct->setChecked(false); if (activeEdges() !=0 ) { statusMessage(tr("Undirected data mode. " "All existing directed edges transformed to " "undirected. Ready") ) ; } else { statusMessage( tr("Undirected data mode. " "Any edge you add will be undirected. Ready")) ; } } else { activeGraph.graphUndirectedSet(toggle); optionsEdgeArrowsAct->trigger(); optionsEdgeArrowsAct->setChecked(true); if (activeEdges() !=0 ) { statusMessage ( tr("Directed data mode. " "All existing undirected edges transformed to " "directed. Ready")) ; } else { statusMessage ( tr("Directed data mode. " "Any new edge you add will be directed. Ready")) ; } } } /** * @brief Toggles between directed and undirected edge mode */ void MainWindow::slotEditEdgeMode(const int &mode){ qDebug()<<"MW: slotEditEdgeMode() - calling Graph::graphUndirectedSet()"; if (mode==1) { activeGraph.graphUndirectedSet(true); optionsEdgeArrowsAct->setChecked(false); if (activeEdges() !=0 ) { statusMessage(tr("Undirected data mode. " "All existing directed edges transformed to " "undirected. Ready") ) ; } else { statusMessage( tr("Undirected data mode. " "Any edge you add will be undirected. Ready")) ; } } else { activeGraph.graphUndirectedSet(false); optionsEdgeArrowsAct->trigger(); optionsEdgeArrowsAct->setChecked(true); if (activeEdges() !=0 ) { statusMessage ( tr("Directed data mode. " "All existing undirected edges transformed to " "directed. Ready")) ; } else { statusMessage ( tr("Directed data mode. " "Any new edge you add will be directed. Ready")) ; } } } /** * Filters Nodes by their value TODO slotFilterNodes * */ void MainWindow::slotFilterNodes(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } } /** * @brief MainWindow::slotEditFilterNodesIsolates *Calls Graph::vertexIsolateFilter to toggle visibility of isolated vertices */ void MainWindow::slotEditFilterNodesIsolates(bool checked){ Q_UNUSED(checked); if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } qDebug()<< "MW: slotEditFilterNodesIsolates"; activeGraph.vertexIsolateFilter( ! editFilterNodesIsolatesAct->isChecked() ); statusMessage( tr("Isolate nodes visibility toggled!") ); } /** * Shows a dialog from where the user may * filter edges according to their weight * All edges weighted more (or less) than the specified weight will be disabled. */ void MainWindow::slotEditFilterEdgesByWeightDialog() { if ( !activeEdges() ) { statusMessage( QString(tr("Load a network file first. \nThen you may ask me to compute something!")) ); return; } m_DialogEdgeFilterByWeight.exec() ; } /** * @brief MainWindow::slotEditFilterEdgesUnilateral * @param checked * Calls Graph::edgeFilterUnilateral( bool). If bool==true, all unilateral * edges are filtered out. */ void MainWindow::slotEditFilterEdgesUnilateral(bool checked) { Q_UNUSED(checked); if ( !activeEdges() && editFilterEdgesUnilateralAct->isChecked() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_EDGES); return; } if (activeGraph.relations()>1) { } qDebug()<< "MW::slotEditFilterEdgesUnilateral"; activeGraph.edgeFilterUnilateral( ! editFilterEdgesUnilateralAct->isChecked() ); statusMessage( tr("Unilateral (weak) edges visibility toggled!") ); } /** * Transforms all nodes to edges TODO slotEditTransformNodes2Edges */ void MainWindow::slotEditTransformNodes2Edges(){ } /** TODO slotLayoutColorationStrongStructural */ void MainWindow::slotLayoutColorationStrongStructural() { } /** TODO slotLayoutColorationRegular */ void MainWindow::slotLayoutColorationRegular() { } /** * @brief MainWindow::slotLayoutRandom * to reposition all nodes on a circular layout randomly */ void MainWindow::slotLayoutRandom(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } graphicsWidget->clearGuides(); statusMessage( tr("Embedding Random Layout. Please wait...") ); progressMsg = tr("Embedding Random Layout. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.layoutRandom(); destroyProgressBar(); statusMessage( tr("Nodes in random positions.") ); } /** * @brief MainWindow::slotLayoutCircularRandom */ void MainWindow::slotLayoutCircularRandom(){ qDebug() << "MainWindow::slotLayoutCircularRandom()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } double x0=scene->width()/2.0; double y0=scene->height()/2.0; double maxRadius=(graphicsWidget->height()/2.0)-50; //pixels slotLayoutGuides(false); statusMessage( QString(tr("Embedding Random Circular model. Please wait...")) ); progressMsg = "Embedding Random Circular model. \n" "Please wait (or disable progress bars from Options -> Settings)."; createProgressBar(0,progressMsg ); activeGraph.layoutCircularRandom(x0, y0, maxRadius); destroyProgressBar(); slotLayoutGuides(true); statusMessage( tr("Nodes in random circles.") ); } /** * @brief MainWindow::slotLayoutSpringEmbedder * Calls Graph::layoutForceDirectedSpringEmbedder to embed a * spring-gravitational model * Called from menu or toolbox checkbox */ void MainWindow::slotLayoutSpringEmbedder(){ qDebug()<< "MW:slotLayoutSpringEmbedder"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } statusMessage( tr("Embedding Spring-Gravitational model (Eades).... ") ); progressMsg = "Embedding Spring-Gravitational model (Eades). \n" "Please wait (or disable progress bars from Options -> Settings)."; createProgressBar(0,progressMsg ); activeGraph.layoutForceDirectedSpringEmbedder(100); destroyProgressBar(); statusMessage( tr("Spring-Gravitational (Eades) model embedded.") ); } /** * @brief MainWindow::slotLayoutFruchterman * Calls Graph::layoutForceDirectedFruchtermanReingold to embed * a repelling-attracting forces model. * Called from menu or toolbox */ void MainWindow::slotLayoutFruchterman(){ qDebug("MW: slotLayoutFruchterman ()"); if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } statusMessage( tr("Embedding a repelling-attracting forces model " "(Fruchterman & Reingold) on the network.... ") ); progressMsg = "Embedding a repelling-attracting forces model " "(Fruchterman & Reingold) \n" "Please wait (or disable progress bars from Options -> Settings)."; createProgressBar(0,progressMsg ); activeGraph.layoutForceDirectedFruchtermanReingold(100); destroyProgressBar(); statusMessage( tr("Fruchterman & Reingold model embedded.") ); } /** * @brief MainWindow::slotLayoutKamadaKawai * Calls Graph::layoutForceDirectedKamadaKawai to embed * a repelling-attracting forces model. * Called from menu or toolbox */ void MainWindow::slotLayoutKamadaKawai(){ qDebug()<< "MW::slotLayoutKamadaKawai ()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } statusMessage( tr("Embedding a dynamic spring model " "(Kamada & Kawai) on the network.... ") ); progressMsg = "Embedding a dynamic spring model " "(Kamada & Kawai) \n" "Please wait (or disable progress bars from Options -> Settings)."; createProgressBar(0,progressMsg ); activeGraph.layoutForceDirectedKamadaKawai(10); destroyProgressBar(); statusMessage( tr("Kamada & Kawai model embedded.") ); } /** * @brief * Resizes all nodes according to their outDegree * Called when user selects the relevant menu entry or the option in the toolbox * @param checked */ void MainWindow::slotLayoutNodeSizesByOutDegree(bool checked){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } qDebug("MW: slotLayoutNodeSizesByOutDegree()"); if (checked != true) { qDebug("MW: slotLayoutNodeSizesByOutDegree() resetting size"); nodeSizesByOutDegreeAct->setChecked(false); toolBoxNodeSizesByOutDegreeBx->setChecked(false); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); activeGraph.layoutVerticesSizeByProminenceIndex( 0, false, false, false); QApplication::restoreOverrideCursor(); return; } qDebug("MW: slotLayoutNodeSizesByOutDegree() setting size"); nodeSizesByOutDegreeAct->setChecked(true); toolBoxNodeSizesByOutDegreeBx->setChecked(true); nodeSizesByInDegreeAct->setChecked(false); toolBoxNodeSizesByInDegreeBx->setChecked(false); askAboutWeights(); statusMessage( tr("Embedding node size model on the network.... ") ); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); activeGraph.layoutVerticesSizeByProminenceIndex( 1,considerWeights,inverseWeights, editFilterNodesIsolatesAct->isChecked()); QApplication::restoreOverrideCursor( ); } /** * @brief * Resizes all nodes according to their inDegree * Called when user selects the relevant menu entry or the option in the toolbox * @param checked */ void MainWindow::slotLayoutNodeSizesByInDegree(bool checked){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } qDebug("MW: slotLayoutNodeSizesByInDegree()"); if (checked != true) { qDebug("MW: slotLayoutNodeSizesByInDegree() resetting size"); nodeSizesByInDegreeAct->setChecked(false); toolBoxNodeSizesByInDegreeBx->setChecked(false); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); activeGraph.layoutVerticesSizeByProminenceIndex( 0, false,false, false); QApplication::restoreOverrideCursor(); return; } qDebug("MW: slotLayoutNodeSizesByInDegree() setting size"); nodeSizesByOutDegreeAct->setChecked(false); toolBoxNodeSizesByOutDegreeBx->setChecked(false); nodeSizesByInDegreeAct->setChecked(true); toolBoxNodeSizesByInDegreeBx->setChecked(true); askAboutWeights(); statusMessage( tr("Embedding node size model on the network.... ") ); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); activeGraph.layoutVerticesSizeByProminenceIndex( 9, considerWeights, inverseWeights, editFilterNodesIsolatesAct->isChecked()); QApplication::restoreOverrideCursor( ); } /** * @brief * Enables/disables layout guides * Called from * @param state */ void MainWindow::slotLayoutGuides(const bool &toggle){ qDebug()<< "MW:slotLayoutGuides()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } if (toggle){ toolBoxLayoutGuidesBx->setCheckState(Qt::Checked); layoutGuidesAct->setChecked(true); qDebug()<< "MW:slotLayoutGuides() - will be displayed"; statusMessage( tr("Layout Guides will be displayed") ); } else { toolBoxLayoutGuidesBx->setCheckState(Qt::Unchecked); layoutGuidesAct->setChecked(false); qDebug()<< "MW:slotLayoutGuides() - will NOT be displayed"; graphicsWidget->clearGuides(); statusMessage( tr("Layout Guides will not be displayed") ); } } /** * @brief * Checks sender text() to find out who QMenu item was pressed * calls slotLayoutCircularByProminenceIndex(QString) */ void MainWindow::slotLayoutCircularByProminenceIndex(){ qDebug() << "MainWindow::slotLayoutCircularByProminenceIndex()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QAction *menuitem=(QAction *) sender(); QString menuItemText=menuitem->text(); qDebug() << "MainWindow::slotLayoutCircularByProminenceIndex() - " << "SENDER MENU IS " << menuItemText; slotLayoutCircularByProminenceIndex(menuItemText); } /** * @brief * Overloaded - called when user clicks Apply in the Layout toolbox * or from slotLayoutCircularByProminenceIndex() when the user click on menu * Repositions all nodes on a Circular layout based on that index * More prominent nodes are closer to the centre of the screen. */ void MainWindow::slotLayoutCircularByProminenceIndex(QString choice=""){ qDebug() << "MainWindow::slotLayoutCircularByProminenceIndex() "; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int userChoice = 0; QString prominenceIndexName = choice; slotLayoutGuides(true); if ( prominenceIndexName.contains("Degree Centr") ) userChoice=1; else if ( prominenceIndexName == "Closeness Centr") userChoice=2; else if ( prominenceIndexName.contains("Influence Range Closeness Centrality") || prominenceIndexName.contains("IR Closeness") ) userChoice=3; else if ( prominenceIndexName.contains("Betweenness Centr")) userChoice=4; else if (prominenceIndexName.contains("Stress Centr")) userChoice=5; else if (prominenceIndexName.contains("Eccentricity Centr")) userChoice=6; else if (prominenceIndexName.contains("Power Centr")) userChoice=7; else if (prominenceIndexName.contains("Information Centr")) userChoice=8; else if (prominenceIndexName.contains("Eigenvector Centr")) userChoice=9; else if (prominenceIndexName.contains("Degree Prestige")) userChoice=10; else if (prominenceIndexName.contains("PageRank Prestige")) userChoice=11; else if (prominenceIndexName.contains("Proximity Prestige")) userChoice=12; qDebug() << "MainWindow::slotLayoutCircularByProminenceIndex() " << "prominenceIndexName " << prominenceIndexName << " userChoice " << userChoice; toolBoxLayoutByIndexSelect->setCurrentIndex(userChoice+1); toolBoxLayoutByIndexTypeSelect->setCurrentIndex(0); bool dropIsolates=false; //check if CC was selected and the graph is disconnected. if (userChoice == 2 ) { int connectedness=activeGraph.graphConnectedness(); switch ( connectedness ) { case 1: break; case 2: break; case -1: QMessageBox::information(this, "Closeness Centrality", tr( "Undirected graph has isolate nodes!\n" "Since this network has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; break; case -3: QMessageBox::information(this, "Closeness Centrality", tr( "Directed graph has isolate nodes!\n" "Since this digraph has isolate nodes, " "I will drop them from calculations" "otherwise Closeness Centrality " "index can not be defined, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can conside using the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; break; default: QMessageBox::critical(this, "Centrality Closeness", tr( "Disconnected graph/digraph!\n" "Since this network is disconnected, " "the ordinary Closeness Centrality " "index is not defined, because d(u,v) will be " "infinite for any isolate nodes u or v.\n" "Please use the slightly different but improved " "Influence Range Closeness (IRCC) index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); return; break; }; } if (userChoice==8 && activeNodes() > 200) { switch( QMessageBox::critical( this, "Slow function warning", tr("Please note that this function is SLOW on large " "networks (n>200), since it will calculate a (n x n) matrix A with:
" "Aii=1+weighted_degree_ni
" "Aij=1 if (i,j)=0
" "Aij=1-wij if (i,j)=wij
" "Next, it will compute the inverse matrix C of A. " "The computation of the inverse matrix is a CPU intensive function " "although it uses LU decomposition.
" "How slow is this? For instance, to compute IC scores of 600 nodes " "on a modern i7 4790K CPU you will need to wait for 2 minutes at least.
" "Are you sure you want to continue?"), QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) ) { case QMessageBox::Ok: break; case QMessageBox::Cancel: // Cancel was clicked return; break; default: // should never be reached break; } } askAboutWeights(); double x0=scene->width()/2.0; double y0=scene->height()/2.0; double maxRadius=(graphicsWidget->height()/2.0)-80; //pixels graphicsWidget->clearGuides(); statusMessage( tr("Embedding Prominence Index Circular Layout. Please wait...") ); progressMsg = tr("Embedding Prominence Index Circular Layout. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.layoutCircularByProminenceIndex( x0, y0, maxRadius, userChoice, considerWeights, inverseWeights, editFilterNodesIsolatesAct->isChecked() || dropIsolates); destroyProgressBar(); statusMessage( tr("Nodes in inner circles have greater prominence index.") ); } /** * @brief * Called when selectbox changes in the toolbox */ void MainWindow::slotLayoutNodeSizesByProminenceIndex(QString choice=""){ qDebug() << "MainWindow::slotLayoutNodeSizesByProminenceIndex() "; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int userChoice = 0; QString prominenceIndexName = choice; if ( prominenceIndexName.contains("Degree Centr") ) userChoice=1; else if ( prominenceIndexName == "Closeness Centr") userChoice=2; else if ( prominenceIndexName.contains("Influence Range Closeness Centrality") || prominenceIndexName.contains("IR Closeness")) userChoice=3; else if ( prominenceIndexName.contains("Betweenness Centr")) userChoice=4; else if (prominenceIndexName.contains("Stress Centr")) userChoice=5; else if (prominenceIndexName.contains("Eccentricity Centr")) userChoice=6; else if (prominenceIndexName.contains("Power Centr")) userChoice=7; else if (prominenceIndexName.contains("Information Centr")) userChoice=8; else if (prominenceIndexName.contains("Eigenvector Centr")) userChoice=9; else if (prominenceIndexName.contains("Degree Prestige")) userChoice=10; else if (prominenceIndexName.contains("PageRank Prestige")) userChoice=11; else if (prominenceIndexName.contains("Proximity Prestige")) userChoice=12; qDebug() << "MainWindow::slotLayoutNodeSizesByProminenceIndex() " << "prominenceIndexName " << prominenceIndexName << " userChoice " << userChoice; toolBoxLayoutByIndexSelect->setCurrentIndex(userChoice+1); toolBoxLayoutByIndexTypeSelect->setCurrentIndex(0); //check if CC was selected and the graph is disconnected. bool dropIsolates=false; if (userChoice == 2 ) { int connectedness=activeGraph.graphConnectedness(); switch ( connectedness ) { case 1: break; case 2: break; case -1: QMessageBox::information(this, "Closeness Centrality", tr( "Undirected graph has isolate nodes!\n" "Since this network has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; break; case -3: QMessageBox::information(this, "Closeness Centrality", tr( "Directed graph has isolate nodes!\n" "Since this digraph has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; break; default: QMessageBox::critical(this, "Centrality Closeness", tr( "Disconnected graph/digraph!\n" "Since this network is disconnected, " "the ordinary Closeness Centrality " "index is not defined, because d(u,v) will be " "infinite for any isolate nodes u or v.\n" "Please use the slightly different but improved " "Influence Range Closeness (IRCC) index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); return; break; }; } if (userChoice==8 && activeNodes() > 200) { switch( QMessageBox::critical( this, "Slow function warning", tr("Please note that this function is SLOW on large " "networks (n>200), since it will calculate a (n x n) matrix A with:
" "Aii=1+weighted_degree_ni
" "Aij=1 if (i,j)=0
" "Aij=1-wij if (i,j)=wij
" "Next, it will compute the inverse matrix C of A. " "The computation of the inverse matrix is a CPU intensive function " "although it uses LU decomposition.
" "How slow is this? For instance, to compute IC scores of 600 nodes " "on a modern i7 4790K CPU you will need to wait for 2 minutes at least.
" "Are you sure you want to continue?"), QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) ) { case QMessageBox::Ok: break; case QMessageBox::Cancel: // Cancel was clicked return; break; default: // should never be reached break; } } askAboutWeights(); graphicsWidget->clearGuides(); statusMessage( tr("Embedding Prominence Index Node Layout. Please wait...") ); progressMsg = tr("Embedding Prominence Index Node Layout. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.layoutVerticesSizeByProminenceIndex( userChoice, considerWeights, inverseWeights, editFilterNodesIsolatesAct->isChecked() || dropIsolates); destroyProgressBar(); statusMessage( tr("Bigger nodes have greater prominence index.") ); } /** * @brief * Checks sender text() to find out who QMenu item was pressed * and what prominence index was chosen * calls slotLayoutLevelByProminenceIndex(QString) */ void MainWindow::slotLayoutLevelByProminenceIndex(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QAction *menuitem=(QAction *) sender(); QString menuItemText = menuitem->text(); qDebug() << "MainWindow::slotLayoutLevelByProminenceIndex() - " << "SENDER MENU IS " << menuItemText; slotLayoutLevelByProminenceIndex(menuItemText); } /** * @brief * Overloaded - called when user clicks on toolbox options and when * the user selects a menu option (called by slotLayoutLevelByProminenceIndex()) * Repositions all nodes on different top-down levels according to the * chosen prominence index. * More prominent nodes are closer to the top of the canvas */ void MainWindow::slotLayoutLevelByProminenceIndex(QString choice=""){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } int userChoice = 0; QString prominenceIndexName = choice; slotLayoutGuides(true); if ( prominenceIndexName.contains("Degree Centr") ) userChoice=1; else if ( prominenceIndexName == "Closeness Centr") userChoice=2; else if ( prominenceIndexName.contains("Influence Range Closeness Centrality") || prominenceIndexName.contains("IR Closeness")) userChoice=3; else if ( prominenceIndexName.contains("Betweenness Centr")) userChoice=4; else if (prominenceIndexName.contains("Stress Centr")) userChoice=5; else if (prominenceIndexName.contains("Eccentricity Centr")) userChoice=6; else if (prominenceIndexName.contains("Power Centr")) userChoice=7; else if (prominenceIndexName.contains("Information Centr")) userChoice=8; else if (prominenceIndexName.contains("Eigenvector Centr")) userChoice=9; else if (prominenceIndexName.contains("Degree Prestige")) userChoice=10; else if (prominenceIndexName.contains("PageRank Prestige")) userChoice=11; else if (prominenceIndexName.contains("Proximity Prestige")) userChoice=12; qDebug() << "MainWindow::slotLayoutLevelByProminenceIndex() " << "prominenceIndexName " << prominenceIndexName << " userChoice " << userChoice; toolBoxLayoutByIndexSelect->setCurrentIndex(userChoice+1); toolBoxLayoutByIndexTypeSelect->setCurrentIndex(1); bool dropIsolates=false; //check if CC was selected and the graph is disconnected. if (userChoice == 2 ) { int connectedness=activeGraph.graphConnectedness(); switch ( connectedness ) { case 1: break; case 2: break; case -1: QMessageBox::information(this, "Closeness Centrality", tr( "Undirected graph has isolate nodes!\n" "Since this network has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; break; case -3: QMessageBox::information(this, "Closeness Centrality", tr( "Directed graph has isolate nodes!\n" "Since this digraph has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; break; default: QMessageBox::critical(this, "Centrality Closeness", tr( "Disconnected graph/digraph!\n" "Since this network is disconnected, " "the ordinary Closeness Centrality " "index is not defined, because d(u,v) will be " "infinite for any isolate nodes u or v.\n" "Please use the slightly different but improved " "Influence Range Closeness (IRCC) index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); return; break; }; } if (userChoice==8 && activeNodes() > 200) { switch( QMessageBox::critical( this, "Slow function warning", tr("Please note that this function is SLOW on large " "networks (n>200), since it will calculate a (n x n) matrix A with:
" "Aii=1+weighted_degree_ni
" "Aij=1 if (i,j)=0
" "Aij=1-wij if (i,j)=wij
" "Next, it will compute the inverse matrix C of A. " "The computation of the inverse matrix is a CPU intensive function " "although it uses LU decomposition.
" "How slow is this? For instance, to compute IC scores of 600 nodes " "on a modern i7 4790K CPU you will need to wait for 2 minutes at least.
" "Are you sure you want to continue?"), QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) ) { case QMessageBox::Ok: break; case QMessageBox::Cancel: // Cancel was clicked return; break; default: // should never be reached break; } } askAboutWeights(); double maxWidth=scene->width(); double maxHeight=scene->height(); graphicsWidget->clearGuides(); statusMessage( tr("Embedding Prominence Index Level Layout. Please wait...") ); progressMsg = tr("Embedding Prominence Index Level Layout. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.layoutLevelByProminenceIndex( maxWidth, maxHeight, userChoice, considerWeights, inverseWeights, editFilterNodesIsolatesAct->isChecked() || dropIsolates); destroyProgressBar(); statusMessage( tr("Nodes in upper levels are more prominent. ") ); } /** * Returns the amount of enabled/active edges on the scene. */ int MainWindow::activeEdges(){ qDebug () << "MW::activeEdges()"; return activeGraph.edgesEnabled(); } /** * Returns the number of active nodes on the scene. */ int MainWindow::activeNodes(){ return activeGraph.vertices(); } /** * Displays a box informing the user about the symmetry or not of the adjacency matrix */ void MainWindow::slotAnalyzeSymmetryCheck(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } if (activeGraph.graphSymmetric()) QMessageBox::information(this, "Symmetry", tr("The adjacency matrix is symmetric." ),"OK",0); else QMessageBox::information(this, "Symmetry", tr("The adjacency matrix is not symmetric." ),"OK",0); statusMessage (QString(tr("Ready")) ); } /** * @brief Writes the adjacency matrix inverse */ void MainWindow::slotAnalyzeMatrixAdjacencyInverse(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-matrix-adjacency-inverse-"+dateTime+".html"; statusMessage(tr ("Inverting adjacency matrix.") ); //activeGraph.writeMatrixAdjacencyInvert(fn, QString("lu")) ; activeGraph.writeMatrix(fn,MATRIX_ADJACENCY_INVERSE) ; if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Inverse matrix saved as: ")+QDir::toNativeSeparators(fn)); } /** * @brief Writes the transpose adjacency matrix */ void MainWindow::slotAnalyzeMatrixAdjacencyTranspose(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-matrix-adjacency-transpose-"+dateTime+".html"; statusMessage( tr ("Transposing adjacency matrix.") ); activeGraph.writeMatrix(fn,MATRIX_ADJACENCY_TRANSPOSE) ; if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Transpose adjacency matrix saved as: ")+QDir::toNativeSeparators(fn)); } /** * @brief Writes the cocitation matrix */ void MainWindow::slotAnalyzeMatrixAdjacencyCocitation(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-matrix-cocitation-"+dateTime+".html"; statusMessage( tr ("Computing Cocitation matrix.") ); activeGraph.writeMatrix(fn,MATRIX_COCITATION) ; if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Cocitation matrix saved as: ")+QDir::toNativeSeparators(fn)); } /** * @brief Writes the degree matrix of the graph */ void MainWindow::slotAnalyzeMatrixDegree(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-matrix-degree-"+dateTime+".html"; statusMessage(tr ("Computing Degree matrix.") ); //activeGraph.writeMatrixDegreeText(fn) ; activeGraph.writeMatrix(fn, MATRIX_DEGREE) ; if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Degree matrix saved as: ")+QDir::toNativeSeparators(fn)); } /** * @brief Writes the Laplacian matrix of the graph */ void MainWindow::slotAnalyzeMatrixLaplacian(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } qDebug() << "MW:slotAnalyzeMatrixLaplacian() - calling Graph::writeMatrix"; QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-matrix-laplacian-"+dateTime+".html"; statusMessage(tr ("Computing Laplacian matrix") ); activeGraph.writeMatrix(fn, MATRIX_LAPLACIAN) ; if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Laplacian matrix saved as: ")+QDir::toNativeSeparators(fn)); } void MainWindow::askAboutWeights(){ qDebug() << "MW::askAboutWeights() - checking if graph weighted."; if (!activeGraph.graphWeighted() ){ considerWeights=false; return; } if (askedAboutWeights) return; if ( ! considerEdgeWeightsAct->isChecked() && !considerWeights){ switch( slotHelpMessageToUser(USER_MSG_QUESTION, tr("Network is edge-weighted. Consider edge weights?"), tr("Edge-weighted network. Consider edge weights?"), tr("The edges in this network have weights (non-unit values). " "Take these edge weights into account to compute distances?"), QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes) ) { case QMessageBox::Yes: considerWeights=true; considerEdgeWeightsAct->setChecked(true); break; case QMessageBox::No: considerWeights=false; considerEdgeWeightsAct->setChecked(false); break; default: // just for sanity considerWeights=false; considerEdgeWeightsAct->setChecked(false); return; break; } } if (considerWeights){ switch( slotHelpMessageToUser( USER_MSG_QUESTION, tr("Inverse edge weights during calculations? "), tr("Inverse edge weights during calculations? "), tr("If the edge weights denote cost or real distances (i.e. miles between cities), " "press No, since the distance between two nodes should be the quickest " "or cheaper one. \n\n" "If the weights denote value or strength (i.e. votes or interaction), " "press Yes to inverse the weights, since the distance between two " "nodes should be the most valuable one."), QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes) ) { case QMessageBox::Yes: inverseWeights=true; break; case QMessageBox::No: inverseWeights=false; break; default: // just for sanity inverseWeights=true; return; break; } } askedAboutWeights=true; } /** * Displays the graph distance (geodesic distance) between two user-specified nodes This is the length of the shortest path between them. */ void MainWindow::slotAnalyzeDistance(){ if ( !activeNodes() || !activeEdges() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } bool ok=false; long int min=1, max=1, i=-1, j=-1; min=activeGraph.vertexNumberMin(); max=activeGraph.vertexNumberMax(); i=QInputDialog::getInt(this, tr("Distance between two nodes"), tr("Select source node: (" +QString::number(min).toLatin1() +"..."+QString::number(max).toLatin1() +"):"), min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( "Distance calculation operation cancelled." ); return; } j=QInputDialog::getInt(this, tr("Distance between two nodes"), tr("Select target node: (" +QString::number(min).toLatin1()+"..." +QString::number(max).toLatin1() +"):"),min, 1, max , 1, &ok ) ; if (!ok) { statusMessage( tr("Distance calculation operation cancelled.") ); return; } qDebug() << "source " << i << " target" << j; if (activeGraph.graphSymmetric() && i>j) { qSwap(i,j); } askAboutWeights(); statusMessage( QString(tr("Computing Graph Distance. Please wait...")) ); progressMsg = tr("Computing Graph Distance. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); int distanceGeodesic = activeGraph.graphDistanceGeodesic(i,j, considerWeights, inverseWeights); destroyProgressBar(); if ( distanceGeodesic > 0 && distanceGeodesic < RAND_MAX) QMessageBox::information(this, tr("Geodesic Distance"), tr("The length of the shortest path between actors (") +QString::number(i)+", "+QString::number(j) +") = "+QString::number(distanceGeodesic) +tr("\nThe nodes are connected."),"OK",0); else QMessageBox::information(this, tr("Geodesic Distance"), tr("Network distance (") +QString::number(i)+", "+QString::number(j) +") = "+ QString("\xE2\x88\x9E") +tr("\nThe nodes are not connected."),"OK",0); } /** * Invokes calculation of the matrix of geodesic distances for the loaded network, then displays it. */ void MainWindow::slotAnalyzeMatrixDistances(){ qDebug() << "MW::slotAnalyzeMatrixDistances()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-matrix-geodesic-distances-"+dateTime+".html"; askAboutWeights(); statusMessage( tr("Computing geodesic distances. Please wait...") ); progressMsg = tr("Computing geodesic distances. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.writeMatrix(fn,MATRIX_DISTANCES, considerWeights, inverseWeights, editFilterNodesIsolatesAct->isChecked()); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Geodesic Distances matrix saved as: ")+QDir::toNativeSeparators(fn)); } /** * @brief Invokes calculation of the geodedics matrix (the number of shortest paths * between each pair of nodes in the loaded network), then displays it. */ void MainWindow::slotAnalyzeMatrixGeodesics(){ qDebug("MW: slotViewNumberOfGeodesics()"); if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-matrix-geodesics-"+dateTime+".html"; askAboutWeights(); statusMessage( tr("Computing geodesics (number of shortest paths). Please wait...") ); progressMsg = tr("Computing geodesics. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); // activeGraph.writeMatrixNumberOfGeodesicsPlainText(fn, // considerWeights, inverseWeights); activeGraph.writeMatrix(fn,MATRIX_GEODESICS, considerWeights, inverseWeights, editFilterNodesIsolatesAct->isChecked()); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Geodesics Matrix saved as: ") + QDir::toNativeSeparators(fn)); } /** Displays the network diameter (largest geodesic) */ void MainWindow::slotAnalyzeDiameter() { if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } askAboutWeights(); statusMessage( QString(tr("Computing Graph Diameter. Please wait...")) ); progressMsg = tr("Computing Graph Diameter. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); int netDiameter=activeGraph.graphDiameter(considerWeights, inverseWeights); destroyProgressBar(); if ( activeGraph.graphWeighted() && considerWeights ) QMessageBox::information(this, "Diameter", tr("Diameter = ") + QString::number(netDiameter) + tr("\n\nSince this is a weighted network \n" "the diameter can be more than N"), "OK",0); else if ( activeGraph.graphWeighted() && !considerWeights ) QMessageBox::information(this, "Diameter", tr("Diameter = ") + QString::number(netDiameter) + tr("\n\nThis is the diameter of the \n" "corresponding network without weights"), "OK",0); else QMessageBox::information(this, "Diameter", tr("Diameter = ") + QString::number(netDiameter) + tr("\n\nSince this is a non-weighted network, \n" "the diameter is always less than N-1."), "OK",0); statusMessage( tr("Graph Diameter computed. Ready.") ); } /** Displays the average shortest path length (average graph distance) */ void MainWindow::slotAnalyzeDistanceAverage() { if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } askAboutWeights(); statusMessage( tr("Computing Average Graph Distance. Please wait...") ); progressMsg = tr("Computing Average Graph Distance. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); float averGraphDistance=activeGraph.graphDistanceGeodesicAverage( considerWeights, inverseWeights, editFilterNodesIsolatesAct->isChecked() ); destroyProgressBar(); QMessageBox::information(this, "Average Graph Distance", "The average shortest path length is = " + QString::number(averGraphDistance), "OK",0); statusMessage( tr("Average geodesic distance computed. Ready.") ); } /** * Writes Eccentricity indices into a file, then displays it. */ void MainWindow::slotAnalyzeEccentricity(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-eccentricity-"+dateTime+".html"; askAboutWeights(); statusMessage( tr("Computing Eccentricities. Please wait...") ); progressMsg = tr("Computing Eccentricities. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.writeEccentricity( fn, considerWeights, inverseWeights, editFilterNodesIsolatesAct->isChecked()); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Eccentricities saved as: ") + QDir::toNativeSeparators(fn) ); } /** * @brief Displays the DialogDissimilarities dialog. */ void MainWindow::slotAnalyzeStrEquivalenceDissimilaritiesDialog() { qDebug()<< "MW::slotAnalyzeStrEquivalenceDissimilaritiesDialog()"; m_dialogdissimilarities = new DialogDissimilarities(this); connect( m_dialogdissimilarities, &DialogDissimilarities::userChoices, this, &MainWindow::slotAnalyzeDissimilaritiesTieProfile ); m_dialogdissimilarities->exec(); } /** * @brief Invokes calculation of pair-wise tie profile dissimilarities of the * network, then displays it. * @param metric * @param varLocation * @param diagonal */ void MainWindow::slotAnalyzeDissimilaritiesTieProfile(const QString &metric, const QString &varLocation, const bool &diagonal){ qDebug() << "MW::slotAnalyzeDissimilaritiesTieProfile()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-matrix-tie-profile-dissimilarities-"+dateTime+".html"; askAboutWeights(); statusMessage( tr("Computing tie profile dissimilarities. Please wait...") ); progressMsg = tr("Computing tie profile dissimilarities. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.writeMatrixDissimilarities(fn, metric, varLocation,diagonal, considerWeights); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Tie profile dissimilarities matrix saved as: ")+QDir::toNativeSeparators(fn)); } /** * @brief Reports the network connectedness */ void MainWindow::slotAnalyzeConnectedness(){ qDebug () << "MW::slotAnalyzeConnectedness()" ; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } statusMessage( QString(tr("Computing Network Connectedness. Please wait...")) ); progressMsg = tr("Computing Network Connectedness. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); int connectedness=activeGraph.graphConnectedness(true); qDebug () << "MW::slotAnalyzeConnectedness result " << connectedness; destroyProgressBar(); switch ( connectedness ) { case 1: QMessageBox::information(this, "Connectedness", "This undirected graph " "is connected.", "OK",0); break; case 0: QMessageBox::information(this, "Connectedness", tr("This undirected graph " " is not connected."), "OK",0); break; case 2: QMessageBox::information(this, "Connectedness", tr("This directed graph " "is strongly connected."), "OK",0); break; case -1: QMessageBox::information(this, "Connectedness", tr("This undirected graph " "is disconnected because isolate nodes exist. \n" "It can become connected by dropping isolates."), "OK",0); break; case -2: QMessageBox::information(this, "Connectedness", tr("This directed graph " "is unilaterally connected. \n" "For every pair of " "nodes (u,v) there is a path either from u to v or " "from v to u, but not always both."), "OK",0); break; case -3: QMessageBox::information(this, "Connectedness", "This directed graph " "is disconnected because isolate nodes exist. \n" "It can become strongly connected by dropping isolates.", "OK",0); break; case -4: QMessageBox::information(this, "Connectedness", "This directed graph " "is disconnected. \nThere are pairs of nodes that " "are disconnected.", "OK",0); break; default: QMessageBox::critical(this, "Connectedness", "Something went wrong!.", "OK",0); break; }; statusMessage( tr("Connectedness calculated. Ready.") ); } /** * Calls Graph:: writeWalksOfLengthMatrixPlainText() to calculate and print * the number of walks of a given length , between each pair of nodes. */ void MainWindow::slotAnalyzeWalksLength(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } bool ok=false; int length = QInputDialog::getInt( this, "Number of walks", tr("Select desired length of walk: (2 to %1)").arg(activeNodes()-1), 2, 2, activeNodes()-1, 1, &ok ); if (!ok) { statusMessage( "Cancelled." ); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-matrix-walks-length-"+QString::number(length)+"-"+dateTime+".html"; statusMessage( tr("Computing walks of length %1. Please wait...").arg(length) ); progressMsg = tr("Computing walks of length %1. \n" "Please wait (or disable progress bars from Options -> Settings).").arg(length); createProgressBar(0,progressMsg); activeGraph.writeMatrixWalks(fn, length); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Walks of length %1 matrix saved as: ").arg(length) + QDir::toNativeSeparators(fn) ); } /** * @brief Calls Graph:: writeWalksTotalMatrixPlainText() to calculate and print * the total number of walks of any length , between each pair of nodes. */ void MainWindow::slotAnalyzeWalksTotal(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } if (activeNodes() > 50) { switch( QMessageBox::critical( this, "Slow function warning", tr("Please note that this function is VERY SLOW on large networks (n>50), " "since it will calculate all powers of the sociomatrix up to n-1 " "in order to find out all possible walks. \n\n" "If you need to make a simple reachability test, " "we advise to use the Reachability Matrix function instead. \n\n" "Are you sure you want to continue?"), QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) ) { case QMessageBox::Ok: break; case QMessageBox::Cancel: // Cancel was clicked return; break; default: // should never be reached break; } } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-matrix-walks-total-"+dateTime+".html"; statusMessage( tr("Computing total walks matrix. Please wait...") ); progressMsg = tr("Computing total walks matrix. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(activeNodes()-1,progressMsg); activeGraph.writeMatrixWalks(fn); destroyProgressBar(activeNodes()-1); // do not check for progress bar if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage("Total walks matrix saved as: " + QDir::toNativeSeparators(fn)); } /** * Calls Graph:: writeReachabilityMatrixPlainText() to calculate and print * the Reachability Matrix of the network. */ void MainWindow::slotAnalyzeReachabilityMatrix(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-matrix-reachability-"+dateTime+".html"; statusMessage( tr("Computing reachability matrix. Please wait...") ); progressMsg = tr("Computing reachability matrix. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); //activeGraph.writeReachabilityMatrixPlainText(fn); activeGraph.writeMatrix(fn, MATRIX_REACHABILITY ); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage("Reachability matrix saved as: " + QDir::toNativeSeparators(fn) ); } /** * @brief Displays the slotAnalyzeStrEquivalenceClusteringHierarchicalDialog dialog. */ void MainWindow::slotAnalyzeStrEquivalenceClusteringHierarchicalDialog() { qDebug()<< "MW::slotAnalyzeStrEquivalenceClusteringHierarchicalDialog()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString preselectMatrix = "Adjacency"; if (!activeGraph.graphWeighted()) { preselectMatrix = "Distances"; } m_dialogClusteringHierarchical = new DialogClusteringHierarchical(this, preselectMatrix); connect( m_dialogClusteringHierarchical, &DialogClusteringHierarchical::userChoices, this, &MainWindow::slotAnalyzeClusteringHierarchical ); m_dialogClusteringHierarchical->exec(); } /** * @brief Called from DialogClusteringHierarchical with user choices. Calls * Graph::writeClusteringHierarchical() to compute and write HCA and displays the report. * @param matrix * @param similarityMeasure * @param linkageCriterion * @param diagonal */ void MainWindow::slotAnalyzeClusteringHierarchical(const QString &matrix, const QString &metric, const QString &method, const bool &diagonal, const bool &diagram){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-clustering-hierarchical-"+dateTime+".html"; bool considerWeights=true; bool inverseWeights=false; bool dropIsolates=true; statusMessage( tr("Computing Hierarchical Cluster Analysis. Please wait...") ); progressMsg = tr("Hierarchical Cluster Analysis... \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.writeClusteringHierarchical(fn, matrix, metric, method, diagonal, diagram, considerWeights, inverseWeights, dropIsolates); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage("Hierarchical Cluster Analysis saved as: " + QDir::toNativeSeparators(fn)); } /** * Calls Graph:: writeCliqueCensus() to write the number of cliques (triangles) * of each vertex into a file, then displays it. */ void MainWindow::slotAnalyzeCommunitiesCliqueCensus(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-clique-census-"+dateTime+".html"; bool considerWeights=true; statusMessage( tr("Computing Clique Census. Please wait...") ); progressMsg = tr("Computing Clique Census. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.writeCliqueCensus(fn, considerWeights); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage("Clique Census saved as: " + QDir::toNativeSeparators(fn)); } /** * @brief Calls Graph::writeClusteringCoefficient() to write Clustering Coefficients * into a file, and displays it. */ void MainWindow::slotAnalyzeClusteringCoefficient (){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-clustering-coefficient-"+dateTime+".html"; bool considerWeights=true; statusMessage( tr("Computing Clustering Coefficients. Please wait...") ); progressMsg = tr("Computing Clustering Coefficient. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.writeClusteringCoefficient(fn, considerWeights); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage("Clustering Coefficients saved as: " + QDir::toNativeSeparators(fn)); } /** * @brief Displays the DialogSimilarityMatches dialog. */ void MainWindow::slotAnalyzeStrEquivalenceSimilarityMeasureDialog() { qDebug()<< "MW::slotAnalyzeStrEquivalenceSimilarityMeasureDialog()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } m_dialogSimilarityMatches = new DialogSimilarityMatches(this); connect( m_dialogSimilarityMatches, &DialogSimilarityMatches::userChoices, this, &MainWindow::slotAnalyzeSimilarityMatching ); m_dialogSimilarityMatches->exec(); } /** * @brief Calls Graph::writeMatrixSimilarityMatching() to write Exact Matches * similarity matrix into a file, and displays it. * */ void MainWindow::slotAnalyzeSimilarityMatching(const QString &matrix, const QString &varLocation, const QString &measure, const bool &diagonal) { if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-matrix-similarity-matches"+dateTime+".html"; bool considerWeights=true; statusMessage( tr("Computing similarity matrix. Please wait...") ); progressMsg = tr("Computing similarity matrix. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); //activeGraph.writeMatrixSimilarityMatchingPlain( fn, measure, matrix, varLocation, diagonal,considerWeights); activeGraph.writeMatrixSimilarityMatching( fn, measure, matrix, varLocation, diagonal, considerWeights); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage("Similarity matrix saved as: " + QDir::toNativeSeparators(fn)); } /** * @brief Calls the m_dialogSimilarityPearson to display the Pearson statistics dialog */ void MainWindow::slotAnalyzeStrEquivalencePearsonDialog(){ qDebug()<< "MW::slotAnalyzeStrEquivalencePearsonDialog()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } m_dialogSimilarityPearson = new DialogSimilarityPearson(this); connect( m_dialogSimilarityPearson, &DialogSimilarityPearson::userChoices, this, &MainWindow::slotAnalyzeSimilarityPearson ); m_dialogSimilarityPearson->exec(); } /** * @brief Calls Graph::writeMatrixSimilarityPearson() to write Pearson * Correlation Coefficients into a file, and displays it. * */ void MainWindow::slotAnalyzeSimilarityPearson(const QString &matrix, const QString &varLocation, const bool &diagonal) { if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-matrix-similarity-pearson-"+dateTime+".html"; bool considerWeights=true; statusMessage( tr("Computing Pearson coefficients matrix. Please wait...") ); progressMsg = tr("Computing Pearson coefficients matrix. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.writeMatrixSimilarityPearson( fn, considerWeights, matrix, varLocation,diagonal); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage("Pearson correlation coefficients matrix saved as: " + QDir::toNativeSeparators(fn)); } /** * Calls Graph to conduct and write a triad census into a file, then displays it. */ void MainWindow::slotAnalyzeCommunitiesTriadCensus() { if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-triad-census-"+dateTime+".html"; bool considerWeights=true; statusMessage( tr("Computing Triad Census. Please wait...") ); progressMsg = tr("Computing Triad Census. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.writeTriadCensus(fn, considerWeights); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage("Triad Census saved as: " + QDir::toNativeSeparators(fn)); } /** * Writes Out-Degree Centralities into a file, then displays it. */ void MainWindow::slotAnalyzeCentralityDegree(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } bool considerWeights=false; if ( activeGraph.graphWeighted()) { switch( slotHelpMessageToUser( USER_MSG_QUESTION, tr("Consider weights?") , tr("Graph edges have weights. \n" "Take weights into account (Default: No)?") ) ) { case QMessageBox::Yes: considerWeights=true; break; case QMessageBox::No: considerWeights=false; break; default: // just for sanity considerWeights=false; return; break; } } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-centrality-out-degree-"+dateTime+".html"; statusMessage( tr("Computing out-Degree Centralities. Please wait...") ); progressMsg = tr("Computing out-Degree Centralities. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.writeCentralityDegree(fn, considerWeights, editFilterNodesIsolatesAct->isChecked() ); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Out-Degree Centralities saved as: ") + QDir::toNativeSeparators(fn)); } /** * Writes Closeness Centralities into a file, then displays it. */ void MainWindow::slotAnalyzeCentralityCloseness(){ qDebug() << "MW::slotAnalyzeCentralityCloseness()"; if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); statusMessage( tr("Please wait while computing Connectivity...") ); int connectedness=activeGraph.graphConnectedness(); QApplication::restoreOverrideCursor(); bool dropIsolates=false; switch ( connectedness ) { case 1: break; case 2: break; case -1: QMessageBox::information(this, "Closeness Centrality", tr( "Undirected graph has isolate nodes!\n" "Since this network has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; break; case -3: QMessageBox::information(this, "Closeness Centrality", tr( "Directed graph has isolate nodes!\n" "Since this digraph has isolate nodes, " "I will drop them from calculations " "otherwise the CC index " "cannot be computed, because d(u,v) will be " "infinite for any isolate node u or v.\n" "You can also try the slightly different " "but improved Influence Range Closeness index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); dropIsolates=true; break; default: QMessageBox::critical(this, "Centrality Closeness", tr( "Disconnected graph/digraph!\n" "Since this network is disconnected, " "the ordinary Closeness Centrality " "index is not defined, because d(u,v) will be " "infinite for any isolate nodes u or v.\n" "Please use the slightly different but improved " "Influence Range Closeness (IRCC) index " "which considers how proximate is each node " "to the nodes in its influence range.\n" "Read more in the SocNetV manual." ), "OK",0); return; break; }; askAboutWeights(); QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-centrality-closeness-"+dateTime+".html"; statusMessage( tr("Computing Closeness Centralities. Please wait...") ); progressMsg = tr("Computing Closeness Centrality. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.writeCentralityCloseness( fn, considerWeights, inverseWeights, editFilterNodesIsolatesAct->isChecked() || dropIsolates); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Closeness Centralities saved as: ") + QDir::toNativeSeparators(fn)); } /** * @brief MainWindow::slotAnalyzeCentralityClosenessIR * Writes Centrality Closeness (based on Influence Range) indices into a file, * then displays it. */ void MainWindow::slotAnalyzeCentralityClosenessIR(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-centrality-closeness-influence-range-"+dateTime+".html"; askAboutWeights(); statusMessage( tr("Computing Influence Range Centralities. Please wait...") ); progressMsg = tr("Computing Influence Range Centrality. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.writeCentralityClosenessInfluenceRange( fn, considerWeights,inverseWeights, editFilterNodesIsolatesAct->isChecked()); destroyProgressBar(); statusMessage( QString(tr(" displaying file..."))); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Influence Range Closeness Centralities saved as: ")+QDir::toNativeSeparators(fn)); } /** * Writes Betweenness Centralities into a file, then displays it. */ void MainWindow::slotAnalyzeCentralityBetweenness(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-centrality-betweenness-"+dateTime+".html"; askAboutWeights(); statusMessage( tr("Computing Betweenness Centralities. Please wait...") ); progressMsg = tr("Computing Betweenness Centrality. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.writeCentralityBetweenness( fn, considerWeights, inverseWeights, editFilterNodesIsolatesAct->isChecked()); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Betweenness Centralities saved as: ")+QDir::toNativeSeparators(fn)); } /** * Writes Degree Prestige indices (In-Degree Centralities) into a file, then displays it. */ void MainWindow::slotAnalyzePrestigeDegree(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } if (activeGraph.graphSymmetric()) { QMessageBox::warning( this, "Warning", tr("Undirected graph!\n" "Degree Prestige counts inbound edges, therefore is more " "meaningful on directed graphs.\n" "For undirected graphs, the DP scores are the same as " "Degree Centrality..."), "OK",0); } bool considerWeights=false; if ( activeGraph.graphWeighted()) { switch( QMessageBox::information( this, "Degree Prestige (In-Degree)", tr("Graph edges have weights. \n" "Take weights into account (Default: No)?"), tr("Yes"), tr("No"), 0, 1 ) ) { case 0: considerWeights=true; break; case 1: considerWeights=false; break; default: // just for sanity considerWeights=false; return; break; } } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-prestige-degree-"+dateTime+".html"; statusMessage( tr("Computing Degree Prestige indices. Please wait...") ); progressMsg = tr("Computing Degree Prestige (in-Degree). \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.writePrestigeDegree(fn, considerWeights, editFilterNodesIsolatesAct->isChecked() ); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Degree Prestige (in-degree) indices saved as: ") + QDir::toNativeSeparators(fn)); } /** * Writes PageRank Prestige indices into a file, then displays it. */ void MainWindow::slotAnalyzePrestigePageRank(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-prestige-pagerank-"+dateTime+".html"; askAboutWeights(); statusMessage( tr("Computing PageRank Prestige indices. Please wait...") ); progressMsg = tr("Computing PageRank Prestige. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.writePrestigePageRank(fn, editFilterNodesIsolatesAct->isChecked()); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("PageRank Prestige indices saved as: ")+ QDir::toNativeSeparators(fn)); } /** * @brief MainWindow::slotAnalyzePrestigeProximity * Writes Proximity Prestige indices into a file, then displays them. */ void MainWindow::slotAnalyzePrestigeProximity(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-prestige-proximity-"+dateTime+".html"; askAboutWeights(); statusMessage( tr("Computing Proximity Prestige indices. Please wait...") ); progressMsg = tr("Computing Proximity Prestige. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.writePrestigeProximity(fn, true, false , editFilterNodesIsolatesAct->isChecked()); destroyProgressBar(); statusMessage( tr("Displaying file...")); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Proximity Prestige indices saved as: ")+ QDir::toNativeSeparators(fn)); } /** * @brief MainWindow::slotAnalyzeCentralityInformation * Writes Informational Centralities into a file, then displays it. */ void MainWindow::slotAnalyzeCentralityInformation(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } if (activeNodes() > 200) { switch( QMessageBox::critical( this, "Slow function warning", tr("Please note that this function is SLOW on large " "networks (n>200), since it will calculate a (n x n) matrix A with:
" "Aii=1+weighted_degree_ni
" "Aij=1 if (i,j)=0
" "Aij=1-wij if (i,j)=wij
" "Next, it will compute the inverse matrix C of A. " "The computation of the inverse matrix is a CPU intensive function " "although it uses LU decomposition.
" "How slow is this? For instance, to compute IC scores of 600 nodes " "on a modern i7 4790K CPU you will need to wait for 2 minutes at least.
" "Are you sure you want to continue?"), QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) ) { case QMessageBox::Ok: break; case QMessageBox::Cancel: // Cancel was clicked return; break; default: // should never be reached break; } } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-centrality-information-"+dateTime+".html"; askAboutWeights(); statusMessage( tr("Computing Information Centralities. Please wait...") ); progressMsg = tr("Computing Information Centrality. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.writeCentralityInformation(fn,considerWeights, inverseWeights); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Information Centralities saved as: ")+ QDir::toNativeSeparators(fn)); } /** * @brief Writes Eigenvector Centralities into a file, then displays it. */ void MainWindow::slotAnalyzeCentralityEigenvector(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-centrality-eigenvector-"+dateTime+".html"; askAboutWeights(); bool dropIsolates = false; statusMessage( tr("Computing Eigenvector Centralities. Please wait...") ); progressMsg = tr("Computing Eigenvector Centrality. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.writeCentralityEigenvector(fn,considerWeights, inverseWeights, dropIsolates); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Eigenvector Centralities saved as: ")+ QDir::toNativeSeparators(fn)); } /** * @brief MainWindow::slotAnalyzeCentralityStress * Writes Stress Centralities into a file, then displays it. */ void MainWindow::slotAnalyzeCentralityStress(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-centrality-stress-"+dateTime+".html"; askAboutWeights(); statusMessage( tr("Computing Stress Centralities. Please wait...") ); progressMsg = tr("Computing Stress Centrality. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.writeCentralityStress( fn, considerWeights, inverseWeights, editFilterNodesIsolatesAct->isChecked()); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Stress Centralities saved as: ")+ QDir::toNativeSeparators(fn)); } /** * @brief MainWindow::slotAnalyzeCentralityPower * Writes Gil-Schmidt Power Centralities into a file, then displays it. */ void MainWindow::slotAnalyzeCentralityPower(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-centrality-power-Gil-Schmidt-"+dateTime+".html"; askAboutWeights(); statusMessage( tr("Computing Gil-Schmidt Power Centralities. Please wait...") ); progressMsg = tr("Computing Gil-Schmidt Power Centrality. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.writeCentralityPower( fn, considerWeights, inverseWeights, editFilterNodesIsolatesAct->isChecked()); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Gil-Schmidt Power Centralities saved as: ")+ QDir::toNativeSeparators(fn)); } /** * @brief MainWindow::slotAnalyzeCentralityEccentricity * Writes Eccentricity Centralities into a file, then displays it. */ void MainWindow::slotAnalyzeCentralityEccentricity(){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QString dateTime=QDateTime::currentDateTime().toString ( QString ("yy-MM-dd-hhmmss")); QString fn = appSettings["dataDir"] + "socnetv-report-centrality-eccentricity-"+dateTime+".html"; askAboutWeights(); statusMessage( tr("Computing Eccentricity Centralities. Please wait...") ); progressMsg = tr("Computing Eccentricity Centrality. \n" "Please wait (or disable progress bars from Options -> Settings)."); createProgressBar(0,progressMsg); activeGraph.writeCentralityEccentricity( fn, considerWeights, inverseWeights, editFilterNodesIsolatesAct->isChecked()); destroyProgressBar(); if ( appSettings["viewReportsInSystemBrowser"] == "true" ) { QDesktopServices::openUrl(QUrl::fromLocalFile(fn)); } else { TextEditor *ed = new TextEditor(fn,this,true); ed->show(); m_textEditors << ed; } statusMessage(tr("Eccentricity Centralities saved as: ")+ QDir::toNativeSeparators(fn)); } /** * @brief MainWindow::createProgressBar * @param max * @param msg * Creates a Qt Progress Dialog * if max = 0, then max becomes equal to active vertices* */ void MainWindow::createProgressBar(const int &max, const QString &msg){ qDebug() << "MW::createProgressBar" ; if ( appSettings["showProgressBar"] == "true" ){ progressDialog = new QProgressDialog(msg, "Cancel", 0, (max==0) ? activeGraph.vertices(): max , this); progressDialog -> setWindowModality(Qt::WindowModal); connect( &activeGraph, SIGNAL( updateProgressDialog(int) ), progressDialog, SLOT(setValue(int) ) ) ; progressDialog->setMinimumDuration(0); } QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); } /** * @brief MainWindow::destroyProgressBar */ void MainWindow::destroyProgressBar(int max){ qDebug () << "MainWindow::destroyProgressBar"; QApplication::restoreOverrideCursor(); qDebug () << "MainWindow::destroyProgressBar - check if a progressbar exists"; if ( appSettings["showProgressBar"] == "true" && max > -1 ) { if ( progressDialog->value() != 0 ) { qDebug () << "MainWindow::destroyProgressBar - progressbar exists. Destroying"; progressDialog->deleteLater(); } } } /** * @brief MainWindow::slotOptionsNodeNumbersVisibility * Turns on/off displaying the numbers of nodes (outside ones) * @param toggle */ void MainWindow::slotOptionsNodeNumbersVisibility(bool toggle) { qDebug() << "MW::slotOptionsNodeNumbersVisibility()" << toggle; QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); statusMessage( tr("Toggle Nodes Numbers. Please wait...") ); appSettings["initNodeNumbersVisibility"] = (toggle) ? "true":"false"; graphicsWidget->setNodeNumberVisibility(toggle); optionsNodeNumbersVisibilityAct->setChecked ( toggle ); if (!toggle) { statusMessage( tr("Node Numbers are invisible now. " "Click the same option again to display them.") ); } else{ statusMessage( tr("Node Numbers are visible again...") ); } QApplication::restoreOverrideCursor(); return; } /** * @brief MainWindow::slotOptionsNodeNumbersInside * Turns on/off displaying the nodenumbers inside the nodes. * @param toggle */ void MainWindow::slotOptionsNodeNumbersInside(bool toggle){ qDebug() << "MW::slotOptionsNodeNumbersInside()" << toggle; statusMessage( tr("Toggle Numbers inside nodes. Please wait...") ); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); // if node numbers are hidden, show them first. if ( toggle && appSettings["initNodeNumbersVisibility"] != "true" ) slotOptionsNodeNumbersVisibility(true); appSettings["initNodeNumbersInside"] = (toggle) ? "true":"false"; activeGraph.vertexNumbersInsideNodesSet(toggle); graphicsWidget -> setNumbersInsideNodes(toggle); optionsNodeNumbersVisibilityAct->setChecked (toggle); if (toggle){ statusMessage( tr("Numbers inside nodes...") ); } else { statusMessage( tr("Numbers outside nodes...") ); } QApplication::restoreOverrideCursor(); } /** * @brief MainWindow::slotOptionsNodeLabelsVisibility * Turns on/off displaying labels * @param toggle */ void MainWindow::slotOptionsNodeLabelsVisibility(bool toggle){ qDebug() << "MW::slotOptionsNodeLabelsVisibility()" << toggle; QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); statusMessage( tr("Toggle Nodes Labels. Please wait...") ); appSettings["initNodeLabelsVisibility"] = (toggle) ? "true":"false"; graphicsWidget->setNodeLabelsVisibility(toggle); activeGraph.vertexLabelsVisibilitySet(toggle); optionsNodeLabelsVisibilityAct->setChecked ( toggle ); if (!toggle) { statusMessage( tr("Node Labels are invisible now. " "Click the same option again to display them.") ); } else{ statusMessage( tr("Node Labels are visible again...") ); } QApplication::restoreOverrideCursor(); } /** * @brief MainWindow::slotOptionsEdgesVisibility * @param toggle */ void MainWindow::slotOptionsEdgesVisibility(bool toggle){ if ( !activeEdges() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); statusMessage( tr("Toggle Edges Arrows. Please wait...") ); appSettings["initEdgesVisibility"] = (toggle) ? "true": "false"; graphicsWidget->setAllItemsVisibility(TypeEdge, toggle); if (!toggle) { statusMessage( tr("Edges are invisible now. Click again the same menu to display them.") ); } else{ statusMessage( tr("Edges visible again...") ); } QApplication::restoreOverrideCursor(); } /** * @brief MainWindow::slotOptionsEdgeArrowsVisibility * Turns on/off the arrows of edges * @param toggle */ void MainWindow::slotOptionsEdgeArrowsVisibility(bool toggle){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } statusMessage( tr("Toggle Edges Arrows. Please wait...") ); appSettings["initEdgeArrows"]= (toggle) ? "true":"false"; if (!toggle) { QList list = scene->items(); for (QList::iterator item=list.begin();item!=list.end(); item++) { if ( (*item)->type() ==TypeEdge){ Edge *edge = (Edge*) (*item); edge->showArrows(false); } } return; } else{ appSettings["initEdgeArrows"]="true"; QList list = scene->items(); for (QList::iterator item=list.begin();item!=list.end(); item++) if ( (*item)->type() ==TypeEdge){ Edge *edge = (Edge*) (*item); edge->showArrows(true); } } statusMessage( tr("Ready.")); } /** * @brief MainWindow::slotOptionsEdgeWeightsDuringComputation * @param toggle */ void MainWindow::slotOptionsEdgeWeightsDuringComputation(bool toggle) { if (toggle) { considerWeights=true; askedAboutWeights=false; askAboutWeights(); // will only ask about inversion } else { considerWeights=false; } activeGraph.graphModifiedSet(GRAPH_CHANGED_EDGES); } /** * FIXME edges Bezier */ void MainWindow::slotOptionsEdgesBezier(bool toggle){ if ( !activeNodes() ) { slotHelpMessageToUser(USER_MSG_CRITICAL_NO_NETWORK); return; } statusMessage( tr("Toggle edges bezier. Please wait...") ); // // graphicsWidget->setBezier(toggle); if (!toggle) { // QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); // QList list = scene->items(); // for (QList::iterator item=list.begin();item!=list.end(); item++) { // if ( (*item)->type() ==TypeEdge ){ // Edge *edge = (Edge*) (*item); // // edge->toggleBezier(false); // (*item)->hide();(*item)->show(); // } // // } // QApplication::restoreOverrideCursor(); // return; } else{ // QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); // QList list = scene->items(); // for (QList::iterator item=list.begin();item!=list.end(); item++){ // if ( (*item)->type() ==TypeEdge ){ // Edge *edge = (Edge*) (*item); // // edge->toggleBezier(true); // (*item)->hide();(*item)->show(); // } // } // QApplication::restoreOverrideCursor(); } } /** * @brief MainWindow::slotOptionsEdgeThicknessPerWeight * @param toggle */ void MainWindow::slotOptionsEdgeThicknessPerWeight(bool toogle) { if (toogle) { } else { } } /** * @brief MainWindow::slotOptionsEdgeWeightNumbersVisibility * Turns on/off displaying edge weight numbers * @param toggle */ void MainWindow::slotOptionsEdgeWeightNumbersVisibility(bool toggle) { qDebug() << "MW::slotOptionsEdgeWeightNumbersVisibility - Toggling Edges Weights"; QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); statusMessage( tr("Toggle Edges Weights. Please wait...") ); appSettings["initEdgeWeightNumbersVisibility"] = (toggle) ? "true":"false"; graphicsWidget->setEdgeWeightNumbersVisibility(toggle); activeGraph.edgeWeightNumbersVisibilitySet(toggle); optionsEdgeWeightNumbersAct->setChecked ( toggle ); if (!toggle) { statusMessage( tr("Edge weights are invisible now. " "Click the same option again to display them.") ); } else{ statusMessage( tr("Edge weights are visible again...") ); } QApplication::restoreOverrideCursor(); } /** * @brief MainWindow::slotOptionsEdgeLabelsVisibility * Turns on/off displaying edge labels * @param toggle */ void MainWindow::slotOptionsEdgeLabelsVisibility(bool toggle) { qDebug() << "MW::slotOptionsEdgeLabelsVisibility - Toggling Edges Weights"; QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); statusMessage( tr("Toggle Edges Labels. Please wait...") ); appSettings["initEdgeLabelsVisibility"] = (toggle) ? "true":"false"; graphicsWidget->setEdgeLabelsVisibility(toggle); activeGraph.edgeLabelsVisibilitySet(toggle); optionsEdgeLabelsAct->setChecked ( toggle ); if (!toggle) { statusMessage( tr("Edge labels are invisible now. " "Click the same option again to display them.") ); } else{ statusMessage( tr("Edge labels are visible again...") ); } QApplication::restoreOverrideCursor(); } /** * @brief MainWindow::slotOptionsAntialiasing * Turns antialiasing on or off * @param toggle */ void MainWindow::slotOptionsAntialiasing(bool toggle) { statusMessage( tr("Toggle anti-aliasing. This will take some time if the network is large (>500)...") ); //Inform graphicsWidget about the change QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); graphicsWidget->setRenderHint(QPainter::Antialiasing, toggle); graphicsWidget->setRenderHint(QPainter::TextAntialiasing, toggle); graphicsWidget->setRenderHint(QPainter::SmoothPixmapTransform, toggle); QApplication::restoreOverrideCursor(); if (!toggle) { statusMessage( tr("Anti-aliasing off.") ); appSettings["antialiasing"] = "false"; } else { appSettings["antialiasing"] = "true"; statusMessage( tr("Anti-aliasing on.") ); } } /** * @brief MainWindow::slotOptionsEmbedLogoExporting * * @param toggle */ void MainWindow::slotOptionsEmbedLogoExporting(bool toggle){ if (!toggle) { statusMessage( tr("SocNetV logo print off.") ); appSettings["printLogo"] = "false"; } else { appSettings["printLogo"] = "true"; statusMessage( tr("SocNetV logo print on.") ); } } /** * @brief MainWindow::slotOptionsProgressBarVisibility * @param toggle * turn progressbar on or off */ void MainWindow::slotOptionsProgressBarVisibility(bool toggle) { statusMessage( tr("Toggle progressbar...")); if (!toggle) { appSettings["showProgressBar"] = "false"; statusMessage( tr("Progress bars off.") ); } else { appSettings["showProgressBar"] = "true"; statusMessage( tr("Progress bars on.") ); } } /** * @brief MainWindow::slotOptionsDebugMessages * @param toggle * Turns debugging messages on or off */ void MainWindow::slotOptionsDebugMessages(bool toggle){ if (!toggle) { appSettings["printDebug"] = "false"; printDebug=false; statusMessage( tr("Debug messages off.") ); } else { appSettings["printDebug"] = "true"; printDebug=true; statusMessage( tr("Debug messages on.") ); } } /** * @brief MainWindow::slotOptionsBackgroundColor * Called from Options menu and Settings dialog * @param color QColor */ void MainWindow::slotOptionsBackgroundColor (QColor color){ if (!color.isValid()) { color = QColorDialog::getColor( QColor ( appSettings["initBackgroundColor"] ), this, "Change the background color" ); } if (color.isValid()) { appSettings["initBackgroundColor"] = color.name(); QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); graphicsWidget ->setBackgroundBrush( QBrush(QColor (appSettings["initBackgroundColor"])) ); QApplication::restoreOverrideCursor(); statusMessage( tr("Ready. ") ); } else { // user pressed Cancel statusMessage( tr("Invalid color. ") ); } } /** * @brief MainWindow::slotOptionsBackgroundImageSelect * Toggles displaying a custom image in the background * If toggle = true, presents a dialog to select an image file * Called from app menu option * @param toggle */ void MainWindow::slotOptionsBackgroundImageSelect(bool toggle) { statusMessage( tr("Toggle BackgroundImage...")); QString m_fileName ; if (toggle == false) { statusMessage( tr("BackgroundImage off.") ); graphicsWidget->setBackgroundBrush( QBrush(QColor (appSettings["initBackgroundColor"] ) ) ); } else { m_fileName = QFileDialog::getOpenFileName( this, tr("Select one image"), getLastPath(), tr("All (*);;PNG (*.png);;JPG (*.jpg)") ); if (m_fileName.isNull() ) appSettings["initBackgroundImage"] = ""; appSettings["initBackgroundImage"] = m_fileName; slotOptionsBackgroundImage(); } } /** * @brief MainWindow::slotOptionsBackgroundImage * Enables/disables displaying a user-defined custom image in the background * Called from Settings Dialog and */ void MainWindow::slotOptionsBackgroundImage() { statusMessage( tr("Toggle BackgroundImage...")); if (appSettings["initBackgroundImage"].isEmpty()) { statusMessage( tr("BackgroundImage off.") ); graphicsWidget->setBackgroundBrush( QBrush(QColor (appSettings["initBackgroundColor"] ) ) ); } else { setLastPath(appSettings["initBackgroundImage"]); graphicsWidget->setBackgroundBrush(QImage(appSettings["initBackgroundImage"])); graphicsWidget->setCacheMode(QGraphicsView::CacheBackground); statusMessage( tr("BackgroundImage on.") ); } } /** * @brief MainWindow::slotOptionsToolbarVisibility * @param toggle * Turns Toolbar on or off */ void MainWindow::slotOptionsToolbarVisibility(bool toggle) { statusMessage( tr("Toggle toolbar...")); if (toggle== false) { toolBar->hide(); appSettings["showToolBar"] = "false"; statusMessage( tr("Toolbar off.") ); } else { toolBar->show(); appSettings["showToolBar"] = "true"; statusMessage( tr("Toolbar on.") ); } } /** * @brief MainWindow::slotOptionsStatusBarVisibility * @param toggle * Turns Statusbar on or off */ void MainWindow::slotOptionsStatusBarVisibility(bool toggle) { statusMessage( tr("Toggle statusbar...")); if (toggle == false) { statusBar()->hide(); appSettings["showStatusBar"] = "false"; statusMessage( tr("Status bar off.") ); } else { statusBar()->show(); appSettings["showStatusBar"] = "true"; statusMessage( tr("Status bar on.") ); } } /** * @brief MainWindow::slotOptionsLeftPanelVisibility * @param toggle */ void MainWindow::slotOptionsLeftPanelVisibility(bool toggle) { statusMessage( tr("Toggle left panel...")); if (toggle == false) { leftPanel->hide(); appSettings["showLeftPanel"] = "false"; statusMessage( tr("Left Panel off.") ); } else { leftPanel->show(); appSettings["showLeftPanel"] = "true"; statusMessage( tr("Left Panel on.") ); } } /** * @brief MainWindow::slotOptionsRightPanelVisibility * @param toggle */ void MainWindow::slotOptionsRightPanelVisibility(bool toggle) { statusMessage( tr("Toggle left panel...")); if (toggle == false) { rightPanel->hide(); appSettings["showRightPanel"] = "false"; statusMessage( tr("Right Panel off.") ); } else { rightPanel->show(); appSettings["showRightPanel"] = "true"; statusMessage( tr("Right Panel on.") ); } } /** * Displays a random tip */ void MainWindow::slotHelpTips() { int randomTip=rand() % (tips.count()); //Pick a tip. QMessageBox::about( this, tr("Tip Of The Day"), tips[randomTip]); } /** Creates our tips. */ void MainWindow::slotHelpCreateTips(){ tips+=tr("To create a new node: \n" "- double-click somewhere on the canvas \n" "- or press the keyboard shortcut CTRL+. (dot)\n" "- or press the Add Node button on the left panel"); tips+=tr("SocNetV supports working with either undirected or directed data. " "When you start SocNetV for the first time, the application uses " "the 'directed data' mode; every edge you create is directed. " "To enter the 'undirected data' mode, press CTRL+E+U or enable the " "menu option Edit -> Edges -> Undirected Edges "); tips+=tr("If your screen is small, and the canvas appears even smaller " "hide the Control and/or Statistics panel. Then the canvas " "will expand to the whole application window. " "Open the Settings/Preferences dialog -> Window options and " "disable the two panels."); tips+=tr("A scale-free network is a network whose degree distribution follows a power law. " "SocNetV generates random scale-free networks according to the " "Barabási–Albert (BA) model using a preferential attachment mechanism."); tips+=tr("To delete a node permanently: \n" "- right-click on it and select Remove Node \n" "- or press CTRL+ALT+. and enter its number\n" "- or press the Remove Node button on the Control Panel"); tips+=tr("To rotate the network: \n" " - drag the bottom slider to left or right \n" " - or click the buttons on the corners of the bottom slider\n" " - or press CTRL and the left or right arrow."); tips+=tr("To create a new edge between nodes A and B: \n" "- double-click on node A, then double-click on node B.\n" "- or middle-click on node A, and again on node B.\n" "- or right-click on the node, then select Add Edge from the popup.\n" "- or press the keyboard shortcut CTRL+/ \n" "- or press the Add Edge button on the Control Panel"); tips+=tr("Add a label to an edge by right-clicking on it " "and selecting Change Label."); tips+=tr("You can change the background color of the canvas. " "Do it from the menu Options > View or " "permanently save this setting in Settings/Preferences."); tips+=tr("Default node colors, shapes and sizes can be changed. " "Open the Settings/Preferences dialog and use the " "options on the Node tab."); tips+=tr("The Statistics Panel shows network-level information (i.e. density) " "as well as info about any node you clicked on (inDegrees, " "outDegrees, clustering)."); tips+=tr("You can move any node by left-clicking and dragging it with your mouse. " "If you want you can move multiple nodes at once. Left-click on empty space " "on the canvas and drag to create a rectangle selection around them. " "Then left-click on one of the selected nodes and drag it."); tips+=tr("To save the node positions in a network, you need to save your data " "in a format which supports node positions, suchs as GraphML or Pajek."); tips+=tr("Embed visualization models on the network from the options in " "the Layout menu or the select boxes on the left Control Panel. "); tips+=tr("To change the label of a node right-click on it, and click " "Selected Node Properties from the popup menu."); tips+=tr("All basic operations of SocNetV are available from the left Control panel " "or by right-clicking on a Node or an Edge or on canvas empty space."); tips+=tr("Node info (number, position, degree, etc) is displayed on the Status bar, " "when you left-click on it."); tips+=tr("Edge information is displayed on the Status bar, when you left-click on it."); tips+=tr("Save your work often, especially when working with large data sets. " "SocNetV alogorithms are faster when working with saved data. "); tips+=tr("The Closeness Centrality (CC) of a node v, is the inverse sum of " "the shortest distances between v and every other node. CC is " "interpreted as the ability to access information through the " "\'grapevine\' of network members. Nodes with high closeness " "centrality are those who can reach many other nodes in few steps. " "This index can be calculated in both graphs and digraphs. " "It can also be calculated in weighted graphs although the weight of " "each edge (v,u) in E is always considered to be 1. "); tips+=tr("The Information Centrality (IC) index counts all paths between " "nodes weighted by strength of tie and distance. " "This centrality measure developed by Stephenson and Zelen (1989) " "focuses on how information might flow through many different paths. " "This index should be calculated only for undirected graphs. " "Note: To compute this index, SocNetV drops all isolated nodes."); } /** * @brief MainWindow::slotHelp * Opens the system web browser to load the online Manual */ void MainWindow::slotHelp(){ statusMessage( tr("Opening the SocNetV Manual in your default web browser....") ); QDesktopServices::openUrl(QUrl("http://socnetv.org/docs/index.html")); } /** * @brief MainWindow::slotHelpCheckUpdates * Opens a web browser to SocNetV website. */ void MainWindow::slotHelpCheckUpdates() { statusMessage( tr("Opening SocNetV website in your default web browser....") ); QDesktopServices::openUrl(QUrl("http://socnetv.org/downloads?app=" + VERSION)); } /** Displays the following message!! */ void MainWindow::slotHelpAbout(){ int randomCookie=rand()%fortuneCookie.count(); QString BUILD="Sat Jan 21 21:37:38 EET 2017"; QMessageBox::about( this, tr("About SocNetV"), tr("Social Network Visualizer (SocNetV)") + tr("

Version: ") + VERSION + "

" + tr("

Build: ") + BUILD + "

" + tr("

Website: http://socnetv.org

")+ tr("

(C) 2005-2017 by Dimitris V. Kalamaras

")+ tr("

Have questions? Contact us!

")+ tr("

Fortune cookie:
\"") + fortuneCookie[randomCookie] + "\"" + tr("

License:

") + tr("

This program is free software; you can redistribute it " "and/or modify it under the terms of the GNU General " "Public License as published by the Free Software Foundation; " "either version 3 of the License, or (at your option) " "any later version.

") + tr("

This program is distributed in the hope that it " "will be useful, but WITHOUT ANY WARRANTY; " "without even the implied warranty of MERCHANTABILITY " "or FITNESS FOR A PARTICULAR PURPOSE. " "See the GNU General Public License for more details.

") + tr("

You should have received a copy of the GNU " "General Public License along with this program; " "If not, see http://www.gnu.org/licenses/

")); } /** Creates the fortune cookies displayed on the above message. */ void MainWindow::createFortuneCookies(){ fortuneCookie+="sic itur ad astra / sic transit gloria mundi ?
" "--Unknown"; fortuneCookie+="Losers of yesterday, the winners of tomorrow...
" "--B.Brecht"; fortuneCookie+="Patriotism is the virtue of the wicked...
" "--O. Wilde"; fortuneCookie+="No tengo nunca mas, no tengo siempre. En la arena
" "la victoria dejo sus piers perdidos.
" "Soy un pobre hombre dispuesto a amar a sus semejantes.
" "No se quien eres. Te amo. No doy, no vendo espinas.
" "--Pablo Neruda" ; fortuneCookie+="Man must not check reason by tradition, but contrawise, " "must check tradition by reason.
--Leo Tolstoy"; fortuneCookie+="Only after the last tree has been cut down,
" "only after the last river has been poisoned,
" "only after the last fish has been caught,
" "only then will you realize that money cannot be eaten.
" "--The Cree People"; fortuneCookie+="Stat rosa pristina nomine, nomina nuda tenemus
" " --Unknown"; fortuneCookie+="Jupiter and Saturn, Oberon, Miranda
" "And Titania, Neptune, Titan.
" "Stars can frighten.
Syd Barrett"; } /** Displays a short message about the Qt Toolbox. */ void MainWindow::slotAboutQt(){ QMessageBox::aboutQt(this, "About Qt - SocNetV"); } socnetv-2.2/src/mainwindow.h000755 000765 000024 00000060573 13040734202 017152 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt mainwindow.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras blog : http://dimitris.apeiro.gr project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef MAINWINDOW_H #define MAINWINDOW_H /** \file mainwindow.h \brief Documentation for the mainwindow file. */ #include #include #include #include #include /** SocNetV specific includes*/ #include "graph.h" #include "graphicswidget.h" #include "dialogfilteredgesbyweight.h" #include "webcrawlerdialog.h" #include "dialognodeedit.h" #include "dialogdatasetselect.h" static const QString VERSION="2.2"; static const int USER_MSG_INFO=0; static const int USER_MSG_CRITICAL=1; static const int USER_MSG_CRITICAL_NO_NETWORK=2; static const int USER_MSG_CRITICAL_NO_EDGES=3; static const int USER_MSG_QUESTION=4; static const int USER_MSG_QUESTION_CUSTOM=5; QT_BEGIN_NAMESPACE class QMenu; class QAction; class QCheckBox; class QProgressDialog; class Edge; class Node; class QPushButton; class QToolButton; class QLCDNumber; class QSlider; class QComboBox; class QGroupBox; class QTabWidget; class QSpinBox; QT_END_NAMESPACE using namespace std; class DialogPreviewFile; class DialogRandErdosRenyi; class DialogRandSmallWorld; class DialogRandScaleFree; class DialogSimilarityPearson; class DialogSimilarityMatches; class DialogDissimilarities; class DialogClusteringHierarchical; class DialogRandRegular; class DialogSettings; class TextEditor; /** \brief The base window of SocNetV contains all widgets and functionality. It sets up the main window and provides a menubar, toolbar and statusbar. For the main view, an instance of class GraphicsWidget is created which creates a graphics widget. */ class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(const QString &f); ~MainWindow(); void initActions(); void initMenuBar(); void initToolBar(); void initToolBox(); void initView(); void initWindowLayout(); void initSignalSlots(); QMap initSettings(); void saveSettings(); void initApp(); void setLastPath(QString filePath); QString getLastPath(); void createFortuneCookies(); int activeEdges(); int activeNodes(); void createProgressBar(const int &max=0, const QString &msg="Please wait..."); void destroyProgressBar(int max=0); public slots: //NETWORK MENU void slotNetworkNew(); void slotNetworkFileChoose(QString m_fileName = QString::null, int m_fileFormat = -1, const bool &checkSelectFileType = true); void slotNetworkFileDialogFileSelected(const QString &fileName); void slotNetworkFileDialogFilterSelected(const QString &filter); void slotNetworkFileDialogRejected(); void slotNetworkFileRecentUpdateActions(); void slotNetworkAvailableTextCodecs(); bool slotNetworkFilePreview(const QString &, const int &); void slotNetworkFileLoad ( const QString, const QString, const int ); void slotNetworkFileLoaded(const int &type, const QString &fName=QString::null, const QString &netName=QString::null, const int &totalNodes=0, const int &totalEdges=0, const QString &message=QString::null); void slotNetworkFileLoadRecent(); void slotNetworkSaved(const int &status); void slotNetworkFileView(); void slotNetworkImportGraphML(); void slotNetworkImportPajek(); void slotNetworkImportSM(); void slotNetworkImportDot(); void slotNetworkImportGML(); void slotNetworkImportDL(); void slotNetworkImportEdgeList(); void slotNetworkImportTwoModeSM(); void slotNetworkChanged(const int &graphStatus, const bool &undirected, const int &vertices, const int &edges, const float &density); void slotNetworkSave(const int &fileFormat=-1); void slotNetworkSaveAs(); void slotNetworkClose(); void slotNetworkPrint(); void slotNetworkViewSociomatrix(); void slotNetworkViewSociomatrixPlotText(); bool slotNetworkExportBMP(); bool slotNetworkExportPNG(); bool slotNetworkExportPDF(); void slotNetworkExportPajek(); void slotNetworkExportSM(); bool slotNetworkExportDL(); bool slotNetworkExportGW(); bool slotNetworkExportList(); void slotNetworkTextEditor(); void slotNetworkDataSetSelect(); void slotNetworkDataSetRecreate(const QString); void slotNetworkRandomErdosRenyiDialog(); void slotNetworkRandomErdosRenyi( const int N, const QString model, const int edges, const float eprob, const QString mode, const bool diag) ; void slotNetworkRandomRegularDialog(); void slotNetworkRandomRegular(const int &newNodes, const int °ree, const QString &mode, const bool &diag); void slotNetworkRandomGaussian(); void slotNetworkRandomScaleFreeDialog(); void slotNetworkRandomScaleFree(const int &newNodes, const int &power, const int &initialNodes, const int &edgesPerStep, const float &zeroAppeal, const QString &mode); void slotNetworkRandomSmallWorldDialog(); void slotNetworkRandomSmallWorld (const int &newNodes, const int °ree, const float &beta, const QString &mode, const bool &diag); void slotNetworkRandomRingLattice(); void slotNetworkWebCrawlerDialog(); void slotNetworkWebCrawler(QString, int, int, bool, bool); //EDIT MENU void slotEditRelationsClear(); void slotEditRelationAdd(QString newRelationName=QString::null, const bool &changeRelation=true); void slotEditRelationChange(const int relIndex=RAND_MAX); void slotEditRelationRename(QString newName=QString::null); void slotEditOpenContextMenu(const QPointF & mPos); void slotEditSelectionChanged (const int &selNodes, const int &selEdges); void slotEditClickOnEmptySpace (const QPointF &p); void slotEditNodeSelectAll(); void slotEditNodeSelectNone(); void slotEditNodeInfoStatusBar(const int &number, const QPointF &p, const QString &label, const int &inDegree, const int &outDegree, const float &clc=0); void slotEditNodePosition(const int &nodeNumber, const int &x, const int &y); void slotEditNodeAdd(); void slotEditNodeAddWithMouse(const QPointF &); void slotEditNodeFind(); void slotEditNodeRemove(); void slotEditNodeOpenContextMenu(); void slotEditNodePropertiesDialog(); void slotEditNodeProperties( const QString, const int, const QString, const QColor, const QString); void slotEditNodeSelectedToClique(); void slotEditNodeSelectedToStar(); void slotEditNodeSelectedToCycle(); void slotEditNodeSelectedToLine(); void slotEditNodeColorAll(QColor color=QColor()); void slotEditNodeSizeAll(int newSize=0, const bool &normalized=false); void slotEditNodeShape(QString shape=QString::null, const int vertex = 0); void slotEditNodeNumberSize(int v1=0, int newSize=0, const bool prompt=true); void slotEditNodeNumberDistance(int v1=0, int newSize=0); void slotEditNodeNumbersColor(QColor color=QColor()); void slotEditNodeLabelSize(int v1=0, int newSize=0); void slotEditNodeLabelsColor(QColor color=QColor()); void slotEditNodeLabelDistance(int v1=0, int newSize=0); void slotEditEdgeInfoStatusBar (const int &v1, const int &v2, const float &weight, const bool &undirected); void slotEditEdgeOpenContextMenu() ; void slotEditEdgeAdd(); void slotEditEdgeCreate (const int &source, const int &target, const float &weight=1); void slotEditEdgeRemove(); void slotEditEdgeLabel(); void slotEditEdgeColor(); void slotEditEdgeWeight(); void slotEditEdgeColorAll(QColor color=QColor(), const int &threshold=RAND_MAX); void slotEditEdgeMode(const int &mode); void slotEditEdgeSymmetrizeAll(); void slotEditEdgeSymmetrizeStrongTies(); void slotEditEdgeSymmetrizeCocitation(); void slotEditEdgeUndirectedAll(const bool &toggle); void slotFilterNodes(); void slotEditFilterNodesIsolates(bool checked); void slotEditFilterEdgesByWeightDialog(); void slotEditFilterEdgesUnilateral(bool checked); void slotEditTransformNodes2Edges(); // LAYOUT MENU void slotLayoutRandom(); void slotLayoutCircularRandom(); void slotLayoutCircularByProminenceIndex(); void slotLayoutCircularByProminenceIndex(QString); void slotLayoutNodeSizesByProminenceIndex(QString); void slotLayoutLevelByProminenceIndex(); void slotLayoutLevelByProminenceIndex(QString); void slotLayoutGuides(const bool &toggle); void slotLayoutSpringEmbedder(); void slotLayoutFruchterman(); void slotLayoutKamadaKawai(); void slotLayoutNodeSizesByOutDegree(bool); void slotLayoutNodeSizesByInDegree(bool); void slotLayoutColorationStrongStructural(); void slotLayoutColorationRegular(); //ANALYSIS MENU void askAboutWeights(); void slotAnalyzeMatrixDistances(); void slotAnalyzeMatrixGeodesics(); void slotAnalyzeDistance(); void slotAnalyzeDistanceAverage(); void slotAnalyzeDiameter(); void slotAnalyzeEccentricity(); void slotAnalyzeStrEquivalenceDissimilaritiesDialog(); void slotAnalyzeDissimilaritiesTieProfile(const QString &metric, const QString &varLocation, const bool &diagonal); void slotAnalyzeWalksLength(); void slotAnalyzeWalksTotal(); void slotAnalyzeReachabilityMatrix(); void slotAnalyzeConnectedness(); void slotAnalyzeStrEquivalenceClusteringHierarchicalDialog(); void slotAnalyzeClusteringHierarchical(const QString &matrix, const QString &metric, const QString &method, const bool &diagonal=false, const bool &diagram=false); void slotAnalyzeCommunitiesCliqueCensus(); void slotAnalyzeClusteringCoefficient(); void slotAnalyzeCommunitiesTriadCensus(); void slotAnalyzeSymmetryCheck(); void slotAnalyzeMatrixAdjacencyInverse(); void slotAnalyzeMatrixAdjacencyTranspose(); void slotAnalyzeMatrixAdjacencyCocitation(); void slotAnalyzeMatrixDegree(); void slotAnalyzeMatrixLaplacian(); void slotAnalyzeCentralityDegree(); void slotAnalyzeCentralityCloseness(); void slotAnalyzeCentralityClosenessIR(); void slotAnalyzeCentralityBetweenness(); void slotAnalyzeCentralityInformation(); void slotAnalyzeCentralityEigenvector(); void slotAnalyzeCentralityStress(); void slotAnalyzeCentralityPower(); void slotAnalyzeCentralityEccentricity(); void slotAnalyzePrestigeDegree(); void slotAnalyzePrestigePageRank(); void slotAnalyzePrestigeProximity(); void slotAnalyzeStrEquivalenceSimilarityMeasureDialog(); void slotAnalyzeSimilarityMatching(const QString &matrix, const QString &varLocation, const QString &measure, const bool &diagonal); void slotAnalyzeStrEquivalencePearsonDialog(); void slotAnalyzeSimilarityPearson(const QString &matrix, const QString &varLocation, const bool &diagonal=false); //OPTIONS MENU void slotOpenSettingsDialog(); void slotOptionsNodeNumbersVisibility(bool toggle); void slotOptionsNodeNumbersInside(bool toggle); void slotOptionsNodeLabelsVisibility(bool toggle); void slotOptionsEdgesVisibility(bool toggle); void slotOptionsEdgeLabelsVisibility(bool toggle); void slotOptionsEdgeWeightNumbersVisibility(bool toggle); void slotOptionsEdgeWeightsDuringComputation(bool); void slotOptionsEdgeThicknessPerWeight(bool toogle); void slotOptionsEdgesBezier(bool toggle); void slotOptionsEdgeArrowsVisibility(bool toggle); void slotOptionsBackgroundColor(QColor color=QColor()); void slotOptionsBackgroundImageSelect(bool toggle); void slotOptionsBackgroundImage(); void slotOptionsAntialiasing(bool ); void slotOptionsEmbedLogoExporting(bool toggle); void slotOptionsProgressBarVisibility(bool toggle); void slotOptionsToolbarVisibility(bool toggle); void slotOptionsStatusBarVisibility(bool toggle); void slotOptionsLeftPanelVisibility(bool toggle); void slotOptionsRightPanelVisibility(bool toggle); void slotOptionsDebugMessages(bool toggle); //HELP MENU void slotHelpTips(); void slotHelp(); void slotHelpCheckUpdates(); void slotHelpCreateTips(); void slotHelpAbout(); void slotAboutQt(); void slotHelpMessageToUserInfo(const QString text=QString::null); void slotHelpMessageToUserError(const QString text=QString::null); int slotHelpMessageToUser(const int type=0, const QString statusMsg=QString::null, const QString text=QString::null, const QString info=QString::null, QMessageBox::StandardButtons buttons=QMessageBox::NoButton, QMessageBox::StandardButton defBtn=QMessageBox::Ok, const QString btn1=QString::null, const QString btn2=QString::null ); //Called by Graph to display some message to the user void statusMessage(const QString); //Called from MW, when user highlights something in the toolbox Comboboxes void toolBoxEditNodeSubgraphSelectChanged(int); void toolBoxEditEdgeSymmetrizeSelectChanged(int); void toolBoxAnalysisMatricesSelectChanged(int); void toolBoxAnalysisCohesionSelectChanged(int); void toolBoxAnalysisStrEquivalenceSelectChanged(int); void toolBoxAnalysisProminenceSelectChanged(int); void toolBoxAnalysisCommunitiesSelectChanged(int); void toolBoxLayoutByIndexButtonPressed(); void toolBoxLayoutForceDirectedButtonPressed(); protected: void resizeEvent( QResizeEvent * ); void closeEvent( QCloseEvent* ce ); // void myMessageOutput(QtMsgType type, const char *msg); signals: void signalRelationAddAndChange(const QString &relName, const bool &changeRelation=true); private: QGraphicsScene *scene; GraphicsWidget *graphicsWidget; Graph activeGraph; QMap appSettings; DialogFilterEdgesByWeight m_DialogEdgeFilterByWeight; WebCrawlerDialog m_WebCrawlerDialog; DialogDataSetSelect m_datasetSelectDialog; DialogNodeEdit *m_nodeEditDialog; DialogRandErdosRenyi *m_randErdosRenyiDialog; DialogRandSmallWorld *m_randSmallWorldDialog; DialogRandScaleFree *m_randScaleFreeDialog; DialogRandRegular *m_randRegularDialog; DialogSettings *m_settingsDialog; DialogSimilarityPearson *m_dialogSimilarityPearson; DialogSimilarityMatches *m_dialogSimilarityMatches; DialogDissimilarities *m_dialogdissimilarities; DialogClusteringHierarchical *m_dialogClusteringHierarchical; DialogPreviewFile *m_dialogPreviewFile; QList codecs; QString userSelectedCodecName; QList m_textEditors; QPrinter *printer; QToolBar *toolBar; QGroupBox *leftPanel, *rightPanel ; QComboBox *editRelationChangeCombo; QProgressDialog *progressDialog; QMenu *importSubMenu, *exportSubMenu, *editMenu, *analysisMenu, *helpMenu; QMenu *optionsMenu, *colorOptionsMenu, *edgeOptionsMenu, *nodeOptionsMenu; QMenu *editNodeMenu, *editEdgeMenu, *centrlMenu, *viewOptionsMenu, *layoutMenu; QMenu *cohesionMenu, *strEquivalenceMenu, *communitiesMenu, *connectivityMenu; QMenu *matrixMenu; QMenu *networkMenu, *randomNetworkMenu, *filterMenu, *recentFilesSubMenu; QMenu *randomLayoutMenu, *circleLayoutMenu, *levelLayoutMenu, *physicalLayoutMenu; QMenu *colorationMenu; QCheckBox *toolBoxNodeSizesByOutDegreeBx,*toolBoxNodeSizesByInDegreeBx, *toolBoxLayoutGuidesBx; QComboBox *toolBoxEditNodeSubgraphSelect, *toolBoxEditEdgeModeSelect, *toolBoxEditEdgeSymmetrizeSelect, *toolBoxAnalysisCohesionSelect, *toolBoxAnalysisStrEquivalenceSelect, *toolBoxAnalysisProminenceSelect, *toolBoxAnalysisCommunitiesSelect, *toolBoxAnalysisMatricesSelect; QComboBox *toolBoxLayoutByIndexSelect, *toolBoxLayoutByIndexTypeSelect; QComboBox *toolBoxLayoutForceDirectedSelect; QPushButton *editNodeAddBt, *editEdgeAddBt, *removeNodeBt, *editEdgeRemoveBt; QPushButton *toolBoxLayoutByIndexButton, *toolBoxLayoutForceDirectedButton; QAction *zoomInAct,*zoomOutAct,*editRotateRightAct,*editRotateLeftAct, *editResetSlidersAct ; QToolButton *zoomInBtn,*zoomOutBtn,*rotateLeftBtn,*rotateRightBtn, *resetSlidersBtn ; QSlider *zoomSlider, *rotateSlider; QAction *networkNew, *networkOpen, *networkSave, *networkSaveAs, *networkClose, *networkPrint,*networkQuit; QAction *networkExportBMP, *networkExportPNG, *networkExportPajek, *networkExportPDF, *networkExportDL, *networkExportGW, *networkExportSM, *networkExportList; QAction *networkImportPajek, *networkImportGML, *networkImportSM, *networkImportList, *networkImportDot , *networkImportDL, *networkImportTwoModeSM; QAction *networkViewFileAct, *openTextEditorAct, *networkViewSociomatrixAct, *networkDataSetSelectAct, *networkViewSociomatrixPlotAct; QAction *createErdosRenyiRandomNetworkAct, *createGaussianRandomNetworkAct; QAction *createLatticeNetworkAct, *createScaleFreeRandomNetworkAct; QAction *createSmallWorldRandomNetworkAct, *createRegularRandomNetworkAct; QAction *optionsNodeNumbersVisibilityAct, *optionsNodeLabelsVisibilityAct, *optionsNodeNumbersInsideAct; QAction *editNodeSelectNoneAct, *editNodeSelectAllAct; QAction *editNodeSelectedToStarAct, *editNodeSelectedToCycleAct; QAction *editNodeSelectedToLineAct, *editNodeSelectedToCliqueAct; QAction *editNodeFindAct,*editNodeAddAct, *editNodeRemoveAct; QAction *editNodePropertiesAct; QAction *editEdgeAddAct, *editEdgeRemoveAct; QAction *editNodeNumbersSizeAct, *editNodeLabelsSizeAct; QAction *editNodeSizeAllAct, *editNodeShapeAll; QAction *editEdgeLabelAct, *editEdgeColorAct, *editEdgeWeightAct; QAction *filterNodesAct, *editFilterNodesIsolatesAct, *editFilterEdgesByWeightAct; QAction *editFilterEdgesUnilateralAct; QAction *transformNodes2EdgesAct, *editEdgeSymmetrizeAllAct; QAction *editEdgeSymmetrizeStrongTiesAct, *editEdgeUndirectedAllAct; QAction *changeBackColorAct, *editNodeColorAll, *editEdgeColorAllAct, *editNodeNumbersColorAct,*editNodeLabelsColorAct, *editEdgesCocitationAct; QAction *optionsEdgeThicknessPerWeightAct, *optionsEdgeWeightNumbersAct, *optionsEdgesVisibilityAct; QAction *optionsEdgeArrowsAct, *drawEdgesBezier,*considerEdgeWeightsAct; QAction *optionsEdgeLabelsAct; QAction *backgroundImageAct,*helpAboutApp, *helpAboutQt, *helpApp, *tipsApp; QAction *helpCheckUpdatesApp; QAction *openSettingsAct; QAction *webCrawlerAct; QAction *netDensity, *analyzeGraphSymmetryAct, *analyzeGraphDistanceAct, *averGraphDistanceAct, *analyzeMatrixDistancesGeodesicAct, *analyzeMatrixGeodesicsAct, *analyzeGraphDiameterAct, *analyzeGraphEccentricityAct; QAction *analyzeStrEquivalenceTieProfileDissimilaritiesAct; QAction *analyzeGraphWalksAct,*analyzeGraphWalksTotalAct, *analyzeMatrixReachabilityAct, *analyzeGraphConnectednessAct; QAction *analyzeCommunitiesCliquesAct, *clusteringCoefAct, *analyzeCommunitiesTriadCensusAct; QAction *analyzeMatrixAdjTransposeAct, *analyzeMatrixAdjInvertAct; QAction *analyzeMatrixAdjCocitationAct; QAction *analyzeMatrixDegreeAct, *analyzeMatrixLaplacianAct; QAction *analyzeStrEquivalenceClusteringHierarchicalAct, *analyzeStrEquivalencePearsonAct; QAction *analyzeStrEquivalenceMatchesAct; QAction *cDegreeAct, *cInDegreeAct, *cClosenessAct, *cInfluenceRangeClosenessAct, *cBetweennessAct, *cInformationAct, *cEigenvectorAct, *cPageRankAct, *cStressAct, *cPowerAct, *cEccentAct, *cProximityPrestigeAct; QAction *randLayoutAct, *randCircleLayoutAct, *layoutGuidesAct; QAction *layoutCircular_DC_Act, *layoutCircular_DP_Act, *layoutCircular_CC_Act, *layoutCircular_SC_Act, *layoutCircular_EC_Act, *layoutCircular_PC_Act, *layoutCircular_BC_Act, *layoutCircular_IC_Act, *layoutCircular_IRCC_Act,*layoutCircular_PRP_Act, *layoutCircular_PP_Act; QAction *layoutLevel_DC_Act, *layoutLevel_DP_Act, *layoutLevel_CC_Act, *layoutLevel_SC_Act, *layoutLevel_EC_Act, *layoutLevel_PC_Act, *layoutLevel_BC_Act, *layoutLevel_IC_Act, *layoutLevel_IRCC_Act,*layoutLevel_PRP_Act, *layoutLevel_PP_Act; QAction *strongColorationAct, *regularColorationAct; QAction *springLayoutAct, *FRLayoutAct; QAction *nodeSizesByOutDegreeAct, *nodeSizesByInDegreeAct; QAction *editRelationNextAct, *editRelationPreviousAct, *editRelationAddAct; QAction *editRelationRenameAct; enum { MaxRecentFiles = 5 }; QAction *recentFileActs[MaxRecentFiles]; QString fileName, previous_fileName, fileNameNoPath, progressMsg; QString settingsFilePath, settingsDir ; QStringList fortuneCookie; QStringList tempFileNameNoPath, tips, recentFiles; int statusBarDuration, progressCounter; int fileType, maxNodes; int fortuneCookiesCounter; //QString VERSION; bool markedNodesExist; bool considerWeights, inverseWeights, askedAboutWeights; float randomErdosEdgeProb; QString initFileCodec; QLabel *rightPanelEdgesLabel, *rightPanelSelectedEdgesLabel, *rightPanelNetworkTypeLabel ; QLabel *rightPanelClickedEdgeHeaderLabel; QLCDNumber *rightPanelClickedNodeInDegreeLCD, *rightPanelClickedNodeOutDegreeLCD; QLCDNumber *rightPanelClickedNodeLCD, *rightPanelClickedNodeClucofLCD; QLCDNumber *rightPanelNodesLCD, *rightPanelEdgesLCD, *rightPanelDensityLCD; QLCDNumber *rightPanelSelectedNodesLCD, *rightPanelSelectedEdgesLCD; QLCDNumber *rightPanelClickedEdgeSourceLCD, *rightPanelClickedEdgeTargetLCD; QLCDNumber *rightPanelClickedEdgeWeightLCD; QDateTime actualDateTime, actualDate, actualTime; QTime eTime; //used to time algorithms. }; #endif socnetv-2.2/src/matrix.cpp000755 000765 000024 00000234413 13040734201 016630 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt matrix - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include "matrix.h" #define TINY 1.0e-20 #include //allows the use of RAND_MAX macro #include #include //needed for fabs, qFloor etc #include /** * @brief Matrix::Matrix * Default constructor - creates a Matrix of given dimension (0x0) * Use resize(m,n) or zeromatrix(m,n) to resize it * @param Actors */ Matrix::Matrix (int rowDim, int colDim) : m_rows (rowDim), m_cols(colDim) { row = new (nothrow) MatrixRow[ m_rows ]; Q_CHECK_PTR( row ); for (int i=0;i 0){ qDebug() << "Matrix::clear() deleting old rows"; m_rows=0; m_cols=0; delete [] row; } } /** * @brief Resizes this matrix to m x n * Called before every operation on new matrices. * Every MatrixRow object holds max_int=32762 * @param Actors */ void Matrix::resize (const int m, const int n) { qDebug() << "Matrix: resize() "; clear(); m_rows = m; m_cols = n; row = new (nothrow) MatrixRow [ m_rows ]; Q_CHECK_PTR( row ); qDebug() << "Matrix: resize() -- resizing each row"; for (int i=0;i max) { max = item(r,c) ; } if ( item(r,c) < min){ min = item(r,c) ; } } } } /** * @brief Like Matrix::findMinMaxValues only it skips r==c * * @param min value. If (r,c) = minimum, it mean that neighbors r and c are the nearest in the matrix/network * @param max value * Complexity: O(n^2) */ void Matrix::NeighboursNearestFarthest (float &min, float & max, int &imin, int &jmin, int &imax, int &jmax){ max=0; min=RAND_MAX; for (int r = 0; r < rows(); ++r) { for (int c = 0; c < cols(); ++c) { if (r==c) continue; if ( item(r,c) > max) { max = item(r,c) ; imax = r; jmax=c; } if ( item(r,c) < min){ min = item(r,c) ; imin = r; jmin=c; } } } } /** * @brief Makes this square matrix the identity square matrix I * @param dim */ void Matrix::identityMatrix(int dim) { qDebug() << "Matrix::identityMatrix() -- deleting old rows"; clear(); m_rows=dim; m_cols=dim; row = new (nothrow) MatrixRow [m_rows]; Q_CHECK_PTR( row ); //qDebug() << "Matrix: resize() -- resizing each row"; for (int i=0;i=m_rows, corner case (will be deleted). Settting to RAND_MAX." // << "New item value (" << i+1 << ", " << j+1 << ") ="<< item(i, j) ; } else if (i=erased) { setItem( i, j, item(i,j+1) ) ; // qDebug() << "Matrix:deleteRowColumn() -" // <<"j>=erased, shifting column left" // << "New item value (" << i+1 << ", " << j+1 << ") ="<< item(i, j) ; } else if (i>=erased && j=erased, shifting rows up." // << "New item value (" << i+1 << ", " << j+1 << ") ="<< item(i, j) ; } else if (i>=erased && j>=erased) { setItem( i, j, item(i+1,j+1) ) ; // qDebug() << "Matrix:deleteRowColumn() -" // <<"both i,j>=erased, shifting row up and column left." // << "New item value (" << i+1 << ", " << j+1 << ") ="<< item(i, j) ; } } row[i].setSize(m_cols); } qDebug() << "Matrix:deleteRowColumn() - finished, new matrix:"; //printMatrixConsole(true); // @TODO comment out to release } /** * @brief Fills a matrix with a given value * @param value */ void Matrix::fillMatrix(float value ) { for (int i=0;i< rows() ; i++) for (int j=0;j< cols(); j++) setItem(i,j, value); } /** * @brief Subtracts this matrix from I and returns * * @return I-this to this matrix */ Matrix& Matrix::subtractFromI () { for (int i=0;i< rows();i++) for (int j=0;jsetItem(i,j, item(i,j)+b.item(i,j)); return *S; } /** * @brief Matrix subtraction, operator - * Subtract this matrix - B of the same dim and returns the result S * Allows S = A-B * @param b * @return Matrix S */ Matrix& Matrix::operator -(Matrix & b) { Matrix *S = new Matrix(rows(), cols() ); qDebug()<< "Matrix::operator -"; for (int i=0;i< rows();i++) for (int j=0;jsetItem(i,j, item(i,j)-b.item(i,j)); return *S; } /** * @brief Matrix multiplication, operator * * Multiplies (right) this matrix with given matrix b. * Allows P = A * B where A,B of same dimension * and returns product as a reference to the calling object * @param b * @param symmetry * @return */ Matrix& Matrix::operator *(Matrix & b) { qDebug()<< "Matrix::operator *"; Matrix *P = new Matrix(rows(), cols()); if ( cols() != b.rows() ) { qDebug()<< "Matrix::product() - ERROR! Non compatible input matrices:" " this(" << rows() << "," << cols() << ") and b(" << b.rows() << ","<< b.cols(); return *P; } for (int i=0;i< rows();i++) for (int j=0;jsetItem(i,j,0); for (int k=0;k< cols();k++) { P->setItem(i,j, P->item(i,j) + item(i,k)*b.item(k,j) ); } } return *P; } /** * @brief Multiplies (right) this m x n matrix with given n x p matrix b * and returns the product in the calling matrix which becomes an m x p matrix. * This convenience operator *= allows A *= B * @param b * @param symmetry * @return */ void Matrix::operator *=(Matrix & b) { qDebug()<< "Matrix::operator *"; if ( cols() != b.rows() ) { qDebug()<< "Matrix::product() - ERROR! Non compatible input matrices:" " this(" << rows() << "," << cols() << ") and b(" << b.rows() << ","<< b.cols(); return; } Matrix *P = new Matrix(rows(), b.cols()); for (int i=0;i< rows();i++) { for (int j=0;jsetItem(i,j,0); for (int k=0;k < cols();k++) { P->setItem(i,j, P->item(i,j) + item(i,k)*b.item(k,j) ); } } } *this = *P; } /** * @brief Matrix Multiplication. Given two matrices A (mxn) and B (nxp) * computes their product and stores it to the calling matrix which becomes * an m x p matrix * Allows P.product(A, B) * @param A * @param B * @param symmetry * @return i x k matrix */ void Matrix::product(Matrix &A, Matrix & B, bool symmetry) { qDebug()<< "Matrix::product() - symmetry" << symmetry; if (A.cols() != B.rows() ) { qDebug()<< "Matrix::product() - ERROR! Non compatible input matrices:" " a(" << A.rows() << "," << A.cols() << ") and b(" << B.rows() << ","<< B.cols(); return; } Matrix *P = new Matrix(A.rows(), B.cols()); float prod = 0; for (int i=0;i< A.rows();i++) { for (int j=0;j j ) continue; prod = 0; for (int k=0;ksetItem(i,j, prod); if (symmetry) { P->setItem(j,i, prod ); } } } // qDebug() << "Matrix::product() - this"; *this = *P; //this->printMatrixConsole(); } /** * @brief Takes two ( N x N ) matrices (symmetric) and outputs an upper triangular matrix * @param a * @param b * @return */ Matrix& Matrix::productSym( Matrix &a, Matrix & b) { for (int i=0;i=j) continue; for (int k=0;k j ) { if (a.item(i,k)!=0 && b.item(j,k)!=0) setItem(i,j, item(i,j)+a.item(i,k)*b.item(j,k)); } else //k <= j && ik ) { if (a.item(k,i)!=0 && b.item(k,j)!=0) setItem(i,j, item(i,j)+a.item(k,i)*b.item(k,j)); } else { if (a.item(i,k)!=0 && b.item(k,j)!=0) setItem(i,j, item(i,j)+a.item(i,k)*b.item(k,j)); } } return *this; } /** * @brief Returns the n-nth power of this matrix * @param n * @param symmetry * @return Matrix */ Matrix& Matrix::pow (int n, bool symmetry) { if (rows()!= cols()) { qDebug()<< "Matrix::pow() - Error. This works only for square matrix"; return *this; } qDebug()<< "Matrix::pow() "; Matrix X, Y; //auxilliary matrices qDebug()<< "Matrix::pow() - creating X = this"; X=*this; //X = this //X.printMatrixConsole(true); qDebug()<< "Matrix::pow() - creating Y = I"; Y.identityMatrix( rows() ); // y=I //Y.printMatrixConsole(true); return expBySquaring2 (Y, X, n, symmetry); } /** * @brief Recursive algorithm implementing "Exponentiation by squaring". * Also known as Fast Modulo Multiplication, this algorithm allows * fast computation of a large power n of square matrix X * @param Y must be the Identity matrix on first call * @param X the matrix to be powered * @param n the power * @param symmetry * @return Matrix& * On first call, parameters must be: Y=I, X the orginal matrix to power and n the power. * Returns the power of matrix X to this object. * For n > 4 it is more efficient than naively multiplying the base with itself repeatedly. */ Matrix& Matrix::expBySquaring2 (Matrix &Y, Matrix &X, int n, bool symmetry) { if (n==1) { qDebug() <<"Matrix::expBySquaring2() - n = 1. Computing PM = X*Y where " "X = " ; //X.printMatrixConsole(); //qDebug() <<"Matrix::expBySquaring2() - n = 1. And Y = "; //Y.printMatrixConsole(); Matrix *PM = new Matrix(rows(), cols()); PM->product(X, Y, symmetry); //qDebug()<<"Matrix::expBySquaring2() - n = 1. PM = X*Y =" ; //PM->printMatrixConsole(); return *PM; } else if ( n%2 == 0 ) { //even qDebug()<<"Matrix::expBySquaring2() - even n =" << n << "Computing PM = X * X"; Matrix PM(rows(), cols()); PM.product(X,X,symmetry); //qDebug()<<"Matrix::expBySquaring2() - even n =" << n << ". PM = X * X = " ; //PM.printMatrixConsole(); return expBySquaring2 ( Y, PM, n/2 ); } else { //odd qDebug()<<"Matrix::expBySquaring2() - odd n =" << n << "First compute PM = X * Y"; Matrix PM(rows(), cols()); Matrix PM2(rows(), cols()); PM.product(X,Y,symmetry); //qDebug()<<"Matrix::expBySquaring2() - odd n =" << n << ". PM = X * Y = " ; //PM.printMatrixConsole(); qDebug()<<"Matrix::expBySquaring2() - odd n =" << n << "Now compute PM2 = X * X"; PM2.product(X,X,symmetry); //qDebug()<<"Matrix::expBySquaring2() - odd n =" << n << ". PM2 = X * X = " ; //PM2.printMatrixConsole(); return expBySquaring2 ( PM, PM2, (n-1)/2 ); } } /** * @brief Calculates the matrix-by-vector product Ax of this matrix * Default product: Ax * if leftMultiply=true then it returns the left product xA * @param in input array/vector * @param out output array * @param leftMultiply */ void Matrix::productByVector (float in[], float out[], const bool &leftMultiply) { int n = rows(); int m = cols(); for(int i = 0; i < n; i++) { out[i] = 0; for (int j = 0; j < m; j++) { if (leftMultiply) { // dot product of row vector b with j-th column in A out[i] += item (j, i) * in[j]; } else { // dot product of i-th row in A with the column vector b out[i] += item (i, j) * in[j]; } } } } /** * @brief Helper function, takes to vectors and returns their * Manhattan distance (also known as l1 norm, Taxicab or L1 distance) * which is the sum of the absolute differences * of their coordinates. * @param x * @param y * @return */ float Matrix::distanceManhattan(float x[], float y[], int n) { float norm = 0; for(int i = 0; i < n; i++) { norm += fabs (x[i] - y[i]); } return norm; } /** * @brief Helper function, computes the Euclideian length (also known as L2 distance) * of a vector: if x = (x1 x2 ... xn), then ||x|| = square_root(x1*x1 + x2*x2 + ... + xn*xn) * @param x * @param n * @return */ float Matrix::distanceEuclidean(float x[], int n) { float norm = 0; for (int i = 0; i < n; i++) { norm += x[i] * x[i]; } norm = sqrt(norm); return norm; } /** * @brief Implementation of the Power method which computes the * leading eigenvector (eigenvector centrality) of this matrix * and returns it as vector x. * @param n * @param x */ void Matrix::powerIteration (float x[], float &xsum, float &xmax, int &xmaxi, float &xmin, int &xmini, const float eps, const int &maxIter) { qDebug() << "Matrix::powerIteration() - maxIter" << maxIter <<"initial x" < vec(n); do { // calculate the matrix-by-vector product Ax and // store the result to vector tmp productByVector(x, tmp, false); // calculate the euclidean length of the resulting vector norm = distanceEuclidean(tmp, n); // normalize tmp to unit vector for next iteration xsum = 0; for(int i = 0; i < n; i++) { tmp[i] = tmp[i] / norm; } // calculate the manhattan distance between the new and prev vectors distance = distanceManhattan (tmp, x, n); vec.clear(); xmax = 0 ; xmin = RAND_MAX; for(int i = 0; i < n; i++) { vec.append(tmp[i]); x[i] = tmp[i]; xsum += x[i]; if (x[i] > xmax) { xmax = x[i] ; xmaxi = i+1; } if (x[i] < xmin) { xmin = x[i] ; xmini = i+1; } } qDebug() << "Matrix::powerIteration() - iteration" << iter << endl << "x" << vec << endl << "distance from previous x " << distance << "sum" << xsum << "xmax" << xmax << "xmin" << xmin; iter ++; if (iter > maxIter) break; } while ( distance > eps); delete [] tmp; } /** * @brief Returns the Transpose of this matrix * Allows T = A.transpose() * @param b * @return Matrix T */ Matrix& Matrix::transpose() { Matrix *T = new Matrix(cols(), rows()); //T->zeroMatrix(cols(), rows()); qDebug()<< "Matrix::transpose()"; for (int i=0;i< cols();i++) { for (int j=0;jsetItem(i,j, item(j,i)); } } return *T; } /** * @brief Returns the Cocitation Matrix of this matrix (C = A * A^T) * Allows T = A.cocitationMatrix() * @param b * @return Matrix T */ Matrix& Matrix::cocitationMatrix() { Matrix *T = new Matrix(cols(), rows()); qDebug()<< "Matrix::cocitationMatrix() this transpose"; //this->transpose().printMatrixConsole(); T->product(this->transpose(),*this, true); return *T; } /** * @brief Returns the Degree Matrix of this matrix. * The Degree Matrix is diagonal matrix which contains information about the degree * of each graph vertex (row of the adjacency matrix) * Allows S = A.degreeMatrix() * @param b * @return Matrix S */ Matrix& Matrix::degreeMatrix() { Matrix *S = new Matrix(rows(), cols()); qDebug()<< "Matrix::degreeMatrix()"; float degree=0; for (int i=0;i< rows();i++) { degree = 0; for (int j=0;jsetItem(i,i, degree); } return *S; } /** * @brief Returns the Laplacian of this matrix. * The Laplacian is a NxN matrix L = D - A where D is the degree matrix of A * Allows S = A.laplacianMatrix() * @param b * @return Matrix S */ Matrix& Matrix::laplacianMatrix() { Matrix *S = new Matrix(rows(), cols()); //S->zeroMatrix(rows(), cols()); qDebug()<< "Matrix::laplacianMatrix()"; *S = (this->degreeMatrix()) - *this; return *S; } /** * @brief Inverts given matrix A by Gauss Jordan elimination Input: matrix A Output: matrix A becomes unit matrix *this becomes the invert of A and is returned back. * @param A * @return inverse matrix of A */ Matrix& Matrix::inverseByGaussJordanElimination(Matrix &A){ qDebug()<< "Matrix::inverseByGaussJordanElimination()"; int n=A.cols(); qDebug()<<"Matrix::inverseByGaussJordanElimination() - build I size " << n << " This will become A^-1 in the end"; identityMatrix( n ); int l=0, m_pivotLine=0; float m_pivot=0, temp_pivot=0, elim_coef=0; for ( int j=0; j< n; j++) { // for n, it is the last diagonal element of A l=j+1; m_pivotLine=-1; m_pivot = A.item(j,j); qDebug() << "inverseByGaussJordanElimination() at column " << j+1 << " Initial pivot " << m_pivot ; for ( int i=l; i qFabs ( m_pivot ) ) { qDebug() << " A("<< i+1 << ","<< j+1 << ") = " << temp_pivot << " absolutely larger than current pivot "<< m_pivot << ". Marking new pivot line: " << i+1; m_pivotLine=i; m_pivot = temp_pivot ; } } if ( m_pivotLine != -1 ) { A.swapRows(m_pivotLine,j); swapRows(m_pivotLine,j); } qDebug()<<" multiplyRow() "<< j+1 << " by value " << 1/m_pivot ; for ( int k=0; k< rows(); k++) { A.setItem ( j, k, (1/m_pivot) * A.item (j, k) ); setItem ( j, k, (1/m_pivot) * item (j, k) ); qDebug()<<" A.item("<< j+1 << ","<< k+1 << ") = " << A.item(j,k); qDebug()<<" item("<< j+1 << ","<< k+1 << ") = " << item(j,k); } qDebug() << "eliminate variables FromRowsBelow()" << j+1 ; for ( int i=0; i< rows(); i++) { qDebug()<<" Eliminating item("<< i+1 << ","<< j+1 << ") = " << A.item(i,j) << " while at column j="<(1,n); float *vv; // vv stores the implicit scaling of each row vv=new (nothrow) float [n]; Q_CHECK_PTR( vv ); // QTextStream stream(stdout); // stream << "a = LU = " << a ; d=1.0; // No row interchanges yet. qDebug () << "Matrix::ludcmp() - loop over row to get scaling info" ; for (i=0;i big) big=temp; } if (big == 0) // No nonzero largest element. { qDebug() << "Matrix::ludcmp() - Singular matrix in routine ludcmp"; return false; } vv[i]=1.0/big; // Save the scaling. qDebug () << "Matrix::ludcmp() - big element in row i " << i+1 << " is "<< big << " row scaling vv[i] " << vv[i]; } qDebug () << "Matrix::ludcmp() - Start Crout's LOOP over columns"; for (j=0;j big) { // Is the figure of merit for the pivot better than the best so far? big=temp; imax=i; qDebug () << "Matrix::ludcmp() - found new largest pivot at row " << imax+1 << " big " << temp; } } qDebug () << "Matrix::ludcmp() - check for row interchange "; if (j != imax) // Do we need to interchange rows? { qDebug () << "Matrix::ludcmp() - interchanging rows " << imax+1 << " and " << j+1; for ( k=0; k=0;i--) { // Now we do the backsubstitution, equation (2.3.7). sum=b[i]; qDebug() << "Matrix::lubksb() backsubstitution: "<< "i " << i << " b[i] " << b[i] << "sum " << sum ; for ( j=i+1;j mean (N,0); // holds mean values qDebug()<< "Matrix::distancesMatrix() -" <<"input matrix"; //printMatrixConsole(true); for (int i = 0 ; i < N ; i++ ) { sum = 0 ; for (int k = i ; k < N ; k++ ) { distTemp = 0; ties = 0; max = 0; for (int j = 0 ; j < N ; j++ ) { if (!diagonal && (i==j || k==j)) continue; switch (metric) { case METRIC_JACCARD_INDEX: if (item(i,j) == item(k,j) && (item(i,j) != 0 && item(i,j) != RAND_MAX)) { distTemp++; } if ( ( item(i,j) != 0 && item(i,j) != RAND_MAX ) || ( item(k,j) != 0 && item(k,j) != RAND_MAX )) { ties++; } break; case METRIC_HAMMING_DISTANCE: if (item(i,j) != item(k,j) ) { distTemp++; } break; case METRIC_EUCLIDEAN_DISTANCE: if (item(i,j) == RAND_MAX || item(k,j) == RAND_MAX || distTemp == RAND_MAX) { distTemp = RAND_MAX; } else { distTemp += ( item(i,j) - item(k,j) )*( item(i,j) - item(k,j) ); //compute (x * y)^2 } break; case METRIC_MANHATTAN_DISTANCE: if (item(i,j) == RAND_MAX || item(k,j) == RAND_MAX || distTemp == RAND_MAX ) { distTemp = RAND_MAX; } else { distTemp += fabs( item(i,j) - item(k,j) ); //compute |x * y| } break; case METRIC_CHEBYSHEV_MAXIMUM: if (item(i,j) == RAND_MAX || item(k,j) == RAND_MAX || distTemp == RAND_MAX) { distTemp = RAND_MAX; max = RAND_MAX; } else { distTemp = fabs( item(i,j) - item(k,j) ); max = ( distTemp > max ) ? distTemp : max; distTemp = max; } break; default: break; } } switch (metric) { case METRIC_JACCARD_INDEX: if (ties!=0) distance = 1 - distTemp/ ( ties ) ; else distance = 1; break; case METRIC_HAMMING_DISTANCE: distance = distTemp; break; case METRIC_EUCLIDEAN_DISTANCE: distance = (distTemp == RAND_MAX) ? distTemp : sqrt(distTemp); break; case METRIC_MANHATTAN_DISTANCE: distance = distTemp ; break; case METRIC_CHEBYSHEV_MAXIMUM: distance = distTemp ; break; default: break; } qDebug() << "distTemp("< mean (N,0); // holds mean values qDebug()<< "Matrix::distancesMatrix() -" <<"input matrix"; //printMatrixConsole(true); for (int i = 0 ; i < N ; i++ ) { sum = 0 ; for (int k = i ; k < N ; k++ ) { distTemp = 0; ties = 0; max = 0; for (int j = 0 ; j < N ; j++ ) { if (!diagonal && (i==j || k==j)) continue; switch (metric) { case METRIC_JACCARD_INDEX: if (item(j,i) == item(j,k) && (item(j,i) != 0 && item(j,i) != RAND_MAX)) { distTemp++; } if ( ( item(j,i) != 0 && item(j,i) != RAND_MAX ) || ( item(j,k) != 0 && item(j,k) != RAND_MAX )) { ties++; } break; case METRIC_HAMMING_DISTANCE: if (item(j,i) != item(j,k) ) { distTemp++; } break; case METRIC_EUCLIDEAN_DISTANCE: if (item(j,i) == RAND_MAX || item(j,k) == RAND_MAX || distTemp == RAND_MAX) { distTemp = RAND_MAX; } else { distTemp += ( item(j,i) - item(j,k) )*( item(j,i) - item(j,k) ); //compute (x * y)^2 } break; case METRIC_MANHATTAN_DISTANCE: if (item(j,i) == RAND_MAX || item(j,k) == RAND_MAX || distTemp == RAND_MAX ) { distTemp = RAND_MAX; } else { distTemp += fabs( item(j,i) - item(j,k) ); //compute |x * y| } break; case METRIC_CHEBYSHEV_MAXIMUM: if (item(j,i) == RAND_MAX || item(j,k) == RAND_MAX || distTemp == RAND_MAX) { distTemp = RAND_MAX; max = RAND_MAX; } else { distTemp = fabs( item(j,i) - item(j,k) ); max = ( distTemp > max ) ? distTemp : max; distTemp = max; } break; default: break; } } switch (metric) { case METRIC_JACCARD_INDEX: if (ties!=0) distance = 1 - distTemp/ ( ties ) ; else distance = 1; break; case METRIC_HAMMING_DISTANCE: distance = distTemp; break; case METRIC_EUCLIDEAN_DISTANCE: distance = (distTemp == RAND_MAX) ? distTemp : sqrt(distTemp); break; case METRIC_MANHATTAN_DISTANCE: distance = distTemp ; break; case METRIC_CHEBYSHEV_MAXIMUM: distance = distTemp ; break; default: break; } qDebug() << "distTemp("< mean (N,0); // holds mean values //create augmented matrix (concatenated rows and columns) from input matrix for (int i = 0 ; i < N ; i++ ) { for (int j = 0 ; j < N ; j++ ) { CM.setItem(j,i, item(i,j)); CM.setItem(j + N,i, item(j,i)); } } qDebug()<< "Matrix::distancesMatrix() -" <<"input matrix"; //CM.printMatrixConsole(true); for (int i = 0 ; i < N ; i++ ) { for (int k = i ; k < N ; k++ ) { distTemp = 0; ties = 0; max = 0; for (int j = 0 ; j < M ; j++ ) { if (!diagonal) { if ( (i==j || k==j )) continue; if ( j>=N && ( (i+N)==j || (k+N)==j )) continue; } switch (metric) { case METRIC_JACCARD_INDEX: if (CM.item(j,i) == CM.item(j,k) && (CM.item(j,i) != 0 && CM.item(j,i) != RAND_MAX)) { distTemp++; } if ( ( CM.item(j,i) != 0 && CM.item(j,i) != RAND_MAX ) || ( CM.item(j,k) != 0 && CM.item(j,k) != RAND_MAX )) { ties++; } break; case METRIC_HAMMING_DISTANCE: if ( CM.item(j,i) != CM.item(j,k) ) { distTemp++; } break; case METRIC_EUCLIDEAN_DISTANCE: if ( CM.item(j,i) == RAND_MAX || CM.item(j,k) == RAND_MAX || distTemp == RAND_MAX) { distTemp = RAND_MAX; } else { distTemp += ( CM.item(j,i) - CM.item(j,k) )*( CM.item(j,i) - CM.item(j,k) ); //compute (x * y)^2 } break; case METRIC_MANHATTAN_DISTANCE: if ( CM.item(j,i) == RAND_MAX || CM.item(j,k) == RAND_MAX || distTemp == RAND_MAX ) { distTemp = RAND_MAX; } else { distTemp += fabs( CM.item(j,i) - CM.item(j,k) ); //compute |x * y| } break; case METRIC_CHEBYSHEV_MAXIMUM: if ( CM.item(j,i) == RAND_MAX || CM.item(j,k) == RAND_MAX || distTemp == RAND_MAX) { distTemp = RAND_MAX; max = RAND_MAX; } else { distTemp = fabs( CM.item(j,i) - CM.item(j,k) ); max = ( distTemp > max ) ? distTemp : max; distTemp = max; } break; default: break; } } switch (metric) { case METRIC_JACCARD_INDEX: if (ties!=0) distance = 1 - distTemp/ ( ties ) ; else distance = 1; break; case METRIC_HAMMING_DISTANCE: distance = distTemp; break; case METRIC_EUCLIDEAN_DISTANCE: distance = (distTemp == RAND_MAX) ? distTemp : sqrt(distTemp); break; case METRIC_MANHATTAN_DISTANCE: distance = distTemp ; break; case METRIC_CHEBYSHEV_MAXIMUM: distance = distTemp ; break; default: break; } qDebug() << "distTemp("<zeroMatrix(N,N); QVector mean (N,0); // holds mean values qDebug()<< "Matrix::similarityMatrix() -" <<"input matrix"; //AM.printMatrixConsole(true); for (int i = 0 ; i < N ; i++ ) { sum = 0 ; for (int k = i ; k < N ; k++ ) { matches = 0; ties = 0; magn_i=0; magn_k=0; for (int j = 0 ; j < N ; j++ ) { if (!diagonal && (i==j || k==j)) continue; switch (measure) { case METRIC_SIMPLE_MATCHING : if (AM.item(i,j) == AM.item(k,j) ) { matches++; } ties++; break; case METRIC_JACCARD_INDEX: if (AM.item(i,j) == AM.item(k,j) && AM.item(i,j) != 0) { matches++; } if (AM.item(i,j) != 0 || AM.item(k,j) ) { ties++; } break; case METRIC_HAMMING_DISTANCE: if (AM.item(i,j) != AM.item(k,j) ) { matches++; } break; case METRIC_COSINE_SIMILARITY: matches += AM.item(i,j) * AM.item(k,j); //compute x * y magn_i += AM.item(i,j) * AM.item(i,j); //compute |x|^2 magn_k += AM.item(k,j) * AM.item(k,j); //compute |y|^2 break; case METRIC_EUCLIDEAN_DISTANCE: matches += ( AM.item(i,j) - AM.item(k,j) )*( AM.item(i,j) - AM.item(k,j) ); //compute (x * y)^2 break; default: break; } } switch (measure) { case METRIC_SIMPLE_MATCHING : matchRatio= matches/ ( ( ties ) ) ; break; case METRIC_JACCARD_INDEX: matchRatio= matches/ ( ( ties ) ) ; break; case METRIC_HAMMING_DISTANCE: matchRatio = matches; break; case METRIC_COSINE_SIMILARITY: // sigma(i,j) = cos(theta) = x * y / |x| * |y| if ( !magn_i || ! magn_k ) { // Note that cosine similarity is undefined when // one or both vertices has degree zero. By convention, // in this case we take sigma(i,j) = 0 matchRatio = 0; } else matchRatio = matches / sqrt( magn_i * magn_k ); break; case METRIC_EUCLIDEAN_DISTANCE: matchRatio = sqrt(matches); break; default: break; } qDebug() << "matches("<zeroMatrix(N,N); QVector mean (N,0); // holds mean values qDebug()<< "Matrix::similarityMatrix() -" <<"input matrix"; //AM.printMatrixConsole(true); for (int i = 0 ; i < N ; i++ ) { sum = 0 ; for (int k = i ; k < N ; k++ ) { matches = 0; ties = 0; magn_i=0; magn_k=0; for (int j = 0 ; j < N ; j++ ) { if (!diagonal && (i==j || k==j)) continue; switch (measure) { case METRIC_SIMPLE_MATCHING : if (AM.item(j,i) == AM.item(j,k) ) { matches++; } ties++; break; case METRIC_JACCARD_INDEX: if (AM.item(j,i) == AM.item(j,k) && AM.item(j,i) != 0) { matches++; } if (AM.item(j,i) != 0 || AM.item(j,k) !=0 ) { ties++; } break; case METRIC_HAMMING_DISTANCE: if (AM.item(j,i) != AM.item(j,k) ) { matches++; } break; case METRIC_COSINE_SIMILARITY: matches += AM.item(j,i) * AM.item(j,k); //compute x * y magn_i += AM.item(j,i) * AM.item(j,i); //compute |x|^2 magn_k += AM.item(j,k) * AM.item(j,k); //compute |y|^2 break; case METRIC_EUCLIDEAN_DISTANCE: matches += ( AM.item(j,i) - AM.item(j,k) )*( AM.item(j,i) - AM.item(j,k) ); //compute (x * y)^2 break; default: break; } } switch (measure) { case METRIC_SIMPLE_MATCHING : matchRatio= matches/ ( ( ties ) ) ; break; case METRIC_JACCARD_INDEX: matchRatio= matches/ ( ( ties ) ) ; break; case METRIC_HAMMING_DISTANCE: matchRatio = matches; break; case METRIC_COSINE_SIMILARITY: // sigma(i,j) = cos(theta) = x * y / |x| * |y| if ( !magn_i || ! magn_k ) { // Note that cosine similarity is undefined when // one or both vertices has degree zero. By convention, // in this case we take sigma(i,j) = 0 matchRatio = 0; } else matchRatio = matches / sqrt( magn_i * magn_k ); break; case METRIC_EUCLIDEAN_DISTANCE: matchRatio = sqrt(matches); break; default: break; } qDebug() << "matches("<zeroMatrix(N,N); CM.zeroMatrix(M,N); QVector mean (N,0); // holds mean values //create augmented matrix (concatenated rows and columns) from input matrix for (int i = 0 ; i < N ; i++ ) { for (int j = 0 ; j < N ; j++ ) { CM.setItem(j,i, AM.item(i,j)); CM.setItem(j + N,i, AM.item(j,i)); } } qDebug()<< "Matrix::similarityMatrix() -" <<"input matrix"; //CM.printMatrixConsole(true); for (int i = 0 ; i < N ; i++ ) { for (int k = i ; k < N ; k++ ) { matches = 0; ties = 0; magn_i=0; magn_k=0; for (int j = 0 ; j < M ; j++ ) { if (!diagonal) { if ( (i==j || k==j )) continue; if ( j>=N && ( (i+N)==j || (k+N)==j )) continue; } switch (measure) { case METRIC_SIMPLE_MATCHING : if (CM.item(j,i) == CM.item(j,k) ) { matches++; } ties++; break; case METRIC_JACCARD_INDEX: if (CM.item(j,i) == CM.item(j,k) && CM.item(j,i) != 0) { matches++; } if (CM.item(j,i) != 0 || CM.item(j,k) !=0 ) { ties++; } break; case METRIC_HAMMING_DISTANCE: if (CM.item(j,i) != CM.item(j,k) ) { matches++; } break; case METRIC_COSINE_SIMILARITY: matches += CM.item(j,i) * CM.item(j,k); //compute x * y magn_i += CM.item(j,i) * CM.item(j,i); //compute |x|^2 magn_k += CM.item(j,k) * CM.item(j,k); //compute |y|^2 break; case METRIC_EUCLIDEAN_DISTANCE: matches += ( CM.item(j,i) - CM.item(j,k) )*( CM.item(j,i) - CM.item(j,k) ); //compute (x * y)^2 break; default: break; } } switch (measure) { case METRIC_SIMPLE_MATCHING : matchRatio= matches/ ( ( ties ) ) ; break; case METRIC_JACCARD_INDEX: matchRatio= matches/ ( ( ties ) ) ; break; case METRIC_HAMMING_DISTANCE: matchRatio = matches; break; case METRIC_COSINE_SIMILARITY: // sigma(i,j) = cos(theta) = x * y / |x| * |y| if ( !magn_i || ! magn_k ) { // Note that cosine similarity is undefined when // one or both vertices has degree zero. By convention, // in this case we take sigma(i,j) = 0 matchRatio = 0; } else matchRatio = matches / sqrt( magn_i * magn_k ); break; case METRIC_EUCLIDEAN_DISTANCE: matchRatio = sqrt(matches); break; default: break; } qDebug() << "matches("<zeroMatrix(N,N); QVector mean (N,0); // holds mean values QVector sigma(N,0); qDebug()<< "Matrix::pearsonCorrelationCoefficients() -" <<"input matrix"; //AM.printMatrixConsole(true); for (int i = 0 ; i < N ; i++ ) { for (int k = i ; k < N ; k++ ) { qDebug() << "comparing rows i"<zeroMatrix(N,N); QVector mean (N,0); // holds mean values QVector sigma(N,0); qDebug()<< "Matrix::pearsonCorrelationCoefficients() -" <<"input matrix"; //AM.printMatrixConsole(true); for (int i = 0 ; i < N ; i++ ) { for (int k = i ; k < N ; k++ ) { qDebug() << "comparing columns i"<zeroMatrix(N,N); CM.zeroMatrix(M,N); QVector mean (N,0); // holds mean values QVector sigma(N,0); //create augmented matrix (concatenated rows and columns) from input matrix for (int i = 0 ; i < N ; i++ ) { for (int j = 0 ; j < N ; j++ ) { CM.setItem(j,i, AM.item(i,j)); CM.setItem(j + N,i, AM.item(j,i)); } } qDebug()<< "Matrix::pearsonCorrelationCoefficients() -" <<"input matrix"; //CM.printMatrixConsole(true); for (int i = 0 ; i < N ; i++ ) { //a column for (int k = i ; k < N ; k++ ) { // next column qDebug() << "comparing augmented columns i"<=N && ( (i+N)==j || (k+N)==j )) { qDebug() << "skipping because j>=N and i"< fabs(maxVal) ) ? fabs(minVal) : fabs(maxVal) ; os << qSetFieldWidth(0) << endl ; os << "- Values: " << ( (hasRealNumbers) ? ("real numbers (printed decimals 3)") : ("integers only" ) ) << endl; os << "- Max value: "; if ( maxVal==RAND_MAX ) os << infinity << " (=not connected nodes, in distance matrix)"; else os << maxVal; os << qSetFieldWidth(0) << endl ; os << "- Min value: "; if ( minVal==RAND_MAX ) os << infinity; else os << minVal; os << qSetFieldWidth(0) << endl << endl; os << qSetFieldWidth(7) << fixed << right << "v"<< qSetFieldWidth(3) << "" ; os << ( (hasRealNumbers) ? qSetRealNumberPrecision(3) : qSetRealNumberPrecision(0) ) ; // Note: In the case of Distance Matrix, // if there is DM(i,j)=RAND_MAX (not connected), we always use fieldWidth = 13 if ( maxAbsVal > 999) fieldWidth = 13 ; else if ( maxAbsVal > 99) fieldWidth = 10 ; else if ( maxAbsVal > 9 ) fieldWidth = 9 ; else fieldWidth = 8 ; // print first/header row for (int r = 0; r < m.cols(); ++r) { actorNumber = r+1; if ( actorNumber > 999) os << qSetFieldWidth(fieldWidth-3) ; else if ( actorNumber > 99) os << qSetFieldWidth(fieldWidth-2) ; else if ( actorNumber > 9) os << qSetFieldWidth(fieldWidth-1) ; else os << qSetFieldWidth(fieldWidth) ; os << fixed << actorNumber; } os << qSetFieldWidth(0) << endl; os << qSetFieldWidth(7)<< endl; // print rows for (int r = 0; r < m.rows(); ++r) { actorNumber = r+1; if ( actorNumber > 999) os << qSetFieldWidth(4) ; else if ( actorNumber > 99) os << qSetFieldWidth(5) ; else if ( actorNumber > 9) os << qSetFieldWidth(6) ; else os << qSetFieldWidth(7) ; os << fixed << actorNumber << qSetFieldWidth(3) <<"" ; for (int c = 0; c < m.cols(); ++c) { element = m(r,c) ; os << qSetFieldWidth(fieldWidth) << fixed << right; if ( element == RAND_MAX) // we print inf symbol instead of RAND_MAX (distances matrix). os << fixed << right << qSetFieldWidth(fieldWidth) << infinity ; else { if ( element > 999) os << qSetFieldWidth(fieldWidth-3) ; else if ( element > 99) os << qSetFieldWidth(fieldWidth-2) ; else if ( element > 9) os << qSetFieldWidth(fieldWidth-1) ; else os << qSetFieldWidth(fieldWidth) ; os << element; } } os << qSetFieldWidth(0) << endl; } return os; } /** * @brief Prints this matrix a HTML table * @param os * @param debug * @return */ bool Matrix::printHTMLTable(QTextStream& os, const bool markDiag, const bool &plain, const bool &printInfinity){ qDebug() << "Matrix::printHTMLTable()"; int actorNumber=0, rowCount = 0; float maxVal, minVal, element; bool hasRealNumbers=false; findMinMaxValues(minVal, maxVal, hasRealNumbers); //maxAbsVal = ( fabs(minVal) > fabs(maxVal) ) ? fabs(minVal) : fabs(maxVal) ; os << ( (hasRealNumbers) ? qSetRealNumberPrecision(3) : qSetRealNumberPrecision(0) ) ; if (plain) { os << "
";

        // print first/header row
        os << "" << qSetFieldWidth(5) << right << "A/A";
        os <<  fixed << qSetFieldWidth(10) << right ;
        for (int r = 0; r < cols(); ++r) {
            actorNumber = r+1;
            os << actorNumber;
        }
        os << qSetFieldWidth(0) << ""<< endl;

        for (int r = 0; r < rows(); ++r) {
            actorNumber = r+1;
            rowCount++;

            os << "" << qSetFieldWidth(5) << right;
            os << actorNumber;
            os << qSetFieldWidth(0) << "";

            for (int c = 0; c < cols(); ++c) {
                element = item(r,c) ;
                os << fixed << qSetFieldWidth(10) << right;
                if (  element == RAND_MAX)  // print inf symbol instead of RAND_MAX (distances matrix).
                    os << infinity;
                else {
                    os << element ;

                }
               // os << "";
            }

            os << qSetFieldWidth(0) << endl;
        }

        os << "
"; return true; } os << "" << "" << "" << ""; // print first/header row for (int r = 0; r < cols(); ++r) { actorNumber = r+1; os << ""; } os << "" << "" << ""; // print rows rowCount = 0; for (int r = 0; r < rows(); ++r) { actorNumber = r+1; rowCount++; os << ""; os <<""; for (int c = 0; c < cols(); ++c) { element = item(r,c) ; os << fixed << right; os <<"" : ">"); if ( ( element == RAND_MAX ) && printInfinity) { // print inf symbol instead of RAND_MAX (distances matrix). os << infinity; } else { os << element ; } os << ""; } os <<""; } os << "
" << ("Actor/Actor") << "" << actorNumber << "
" << actorNumber << "
"; os << qSetFieldWidth(0) << endl ; os << "

" << "" << ("Values: ") <<"" << ( (hasRealNumbers) ? ("real numbers (printed decimals 3)") : ("integers only" ) ) << "
" << "" << ("- Max value: ") <<"" << ( ( maxVal==RAND_MAX ) ? ( (printInfinity) ? infinity : QString::number(maxVal) ) + " (=not connected nodes, in distance matrix)" : QString::number(maxVal) ) << "
" << "" << ("- Min value: ") <<"" << ( ( minVal==RAND_MAX ) ? ( (printInfinity) ? infinity : QString::number(minVal) ) + + " (usually denotes unconnected nodes, in distance matrix)" : QString::number(minVal ) ) << "

"; return true; } /** * @brief Prints this matrix to stderr or stdout * @return */ bool Matrix::printMatrixConsole(bool debug){ //qDebug() << "Matrix::printMatrixConsole() debug " << debug ; QTextStream out ( (debug ? stderr : stdout) ); for (int r = 0; r < rows(); ++r) { for (int c = 0; c < cols(); ++c) { if ( item(r,c) < RAND_MAX ) { out << qSetFieldWidth(12) << qSetRealNumberPrecision(3) << forcepoint << fixed<. * ********************************************************************************/ #ifndef MATRIX_H #define MATRIX_H #include #include //for static const QString declares below #include // std::pair, std::make_pair using namespace std; //or else compiler groans for nothrow class QTextStream; #ifdef Q_OS_WIN32 static const QString infinity = "\u221E" ; #else static const QString infinity = QString("\xE2\x88\x9E") ; #endif static const int METRIC_NONE = -1; static const int METRIC_SIMPLE_MATCHING = 0; static const int METRIC_JACCARD_INDEX = 1; static const int METRIC_HAMMING_DISTANCE = 2; static const int METRIC_COSINE_SIMILARITY = 3; static const int METRIC_EUCLIDEAN_DISTANCE = 4; static const int METRIC_MANHATTAN_DISTANCE= 5; static const int METRIC_PEARSON_COEFFICIENT = 6; static const int METRIC_CHEBYSHEV_MAXIMUM= 7; class MatrixRow { public: MatrixRow (int cols=0) { cell=new (nothrow) float [m_cols=cols]; Q_CHECK_PTR( cell ); for (int i=0;i. * ********************************************************************************/ #include "node.h" #include #include #include #include #include #include #include "graphicswidget.h" #include "edge.h" #include "nodelabel.h" #include "nodenumber.h" #include Node::Node(GraphicsWidget* gw, const int &num, const int &size, const QString &color, const QString &shape, const bool &showNumbers, const bool &numbersInside, const QString &numberColor, const int &numberSize, const int &numDistance, const bool &showLabels, const QString &label, const QString &labelColor, const int &labelSize, const int &labelDistance, QPointF p ) : graphicsWidget (gw) { qDebug()<<"Node::Node()"; graphicsWidget->scene()->addItem(this); //Without this nodes don't appear on the screen... setFlags(ItemSendsGeometryChanges | ItemIsSelectable | ItemIsMovable ); //setCacheMode(QGraphicsItem::ItemCoordinateCache); //QT < 4.6 if a cache mode is set, nodes do not respond to hover events setCacheMode(QGraphicsItem::NoCache); //QT < 4.6 if a cache mode is set, nodes do not respond to hover events setAcceptHoverEvents(true); m_num=num; m_size=size; m_shape=shape; m_col_str=color; m_col=QColor(color); m_hasNumber=showNumbers; m_hasNumberInside = numbersInside; m_numSize = numberSize; m_numColor = numberColor; m_numberDistance=numDistance; m_hasLabel=showLabels; m_labelText = label; m_labelSize = labelSize; m_labelColor = labelColor; m_labelDistance=labelDistance; if (m_hasLabel) { addLabel(); } if (!m_hasNumberInside && m_hasNumber) { addNumber(); } setShape(m_shape); setPos(p); qDebug()<< "Node::Node() - Node created at position:" << x()<<","<setEndOffset(size); } foreach (Edge *edge, outEdgeList) { qDebug("Node: updating edges in outEdgeList"); edge->setStartOffset(size); } setShape(m_shape); } /** Used by MainWindow::findNode() and Edge::Edge() */ int Node::size() const{ qDebug("size()"); return m_size; } /** Called every time the user needs to change the shape of an node. */ void Node::setShape(const QString shape) { prepareGeometryChange(); m_shape=shape; qDebug()<< "Node::setShape() - Node " << nodeNumber() << "shape" << m_shape << "pos "<< x() << "," << y(); m_path = new QPainterPath; if ( m_shape == "circle") { m_path->addEllipse (-m_size, -m_size, 2*m_size, 2*m_size); } else if ( m_shape == "ellipse") { m_path->addEllipse(-m_size, -m_size, 2*m_size, 1.7* m_size); } else if ( m_shape == "box" || m_shape == "rectangle" ) { //rectangle: for GraphML/GML compliance m_path->addRect (-m_size , -m_size , 1.8*m_size , 1.8*m_size ); } else if (m_shape == "roundrectangle" ) { //roundrectangle: GraphML only m_path->addRoundedRect (-m_size , -m_size , 1.8*m_size , 1.8*m_size, 60.0, 60.0, Qt::RelativeSize ); } else if ( m_shape == "triangle") { m_path->moveTo(-m_size,0.95* m_size) ; m_path->lineTo(m_size,0.95*m_size); m_path->lineTo( 0,-1*m_size); m_path->lineTo(-m_size,0.95*m_size) ; m_path->closeSubpath(); } else if ( m_shape == "star") { m_path->setFillRule(Qt::WindingFill); m_path->moveTo(-0.8*m_size,0.6* m_size) ; m_path->lineTo(+0.8*m_size,0.6*m_size); m_path->lineTo( 0,-1*m_size); m_path->lineTo(-0.8*m_size,0.6*m_size) ; m_path->closeSubpath(); m_path->moveTo(0, 1* m_size) ; m_path->lineTo(+0.8*m_size,-0.6*m_size); m_path->lineTo(-0.8*m_size,-0.6*m_size) ; m_path->lineTo(0, 1* m_size); m_path->closeSubpath(); } else if ( m_shape == "diamond"){ m_path->moveTo(-m_size, 0); m_path->lineTo( 0,-1*m_size); m_path->lineTo( m_size, 0); m_path->lineTo( 0, 1*m_size); m_path->lineTo(-m_size, 0) ; m_path->closeSubpath(); } update(); } /* * Returns the shape of the node as a path (an accurate outline of the item's shape) * Used by the collision algorithm in collidesWithItem() */ QPainterPath Node::shape() const { //qDebug ("Node: shape()"); return (*m_path); } /* * Returns the bounding rectangle of the node * That is the rectangle where all painting will take place. */ QRectF Node::boundingRect() const { qreal adjust = 5; return QRectF(-m_size -adjust , -m_size-adjust , 2*m_size+adjust , 2*m_size +adjust); } /** */ /** * @brief Node::paint * Does the actual painting using the QPainterPath created by the setShape() * Called by GraphicsView and Node methods in every update() * @param painter * @param option */ void Node::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) { // painter->setClipRect( option->exposedRect ); //if the node is being dragged around, darken it! if (option->state & QStyle::State_Selected) { //qDebug()<< " node : selected "; painter->setBrush(m_col.dark(150)); setZValue(ZValueNodeHighlighted); } else if (option->state & QStyle::State_MouseOver) { //qDebug()<< " node : mouse over"; painter->setBrush(m_col.dark(150)); setZValue(ZValueNodeHighlighted); } //else if (option->state & QStyle::State_Sunken) { //qDebug()<< " node : sunken "; //painter->setBrush(m_col_dark.dark(160)); //} else { //no, just paint it with the usual color. //qDebug()<< " node : nothing"; setZValue(ZValueNode); painter->setBrush(m_col); } painter->setPen(QPen(QColor("#222"), 0)); painter->drawPath (*m_path); if (m_hasNumberInside && m_hasNumber) { // m_path->setFillRule(Qt::WindingFill); painter->setPen(QPen(QColor(m_numColor), 0)); if (m_num > 999) { painter->setFont(QFont("Sans Serif", (m_numSize)? m_numSize-1: 0.66*m_size, QFont::Normal)); painter->drawText(-0.8*m_size,m_size/3, QString::number(m_num) ); } else if (m_num > 99) { painter->setFont(QFont("Sans Serif", (m_numSize)? m_numSize-1: 0.66*m_size, QFont::Normal)); painter->drawText(-0.6 * m_size,m_size/3, QString::number(m_num) ); } else if (m_num > 9 ) { painter->setFont(QFont("Sans Serif", (m_numSize)? m_numSize: 0.66*m_size, QFont::Normal)); painter->drawText(-0.5*m_size,m_size/3,QString::number(m_num) ); } else { painter->setFont(QFont("Sans Serif", (m_numSize)? m_numSize: 0.66*m_size, QFont::Normal)); painter->drawText(-0.33*m_size,m_size/3,QString::number(m_num) ); } } } /** * @brief Node::itemChange * Called when the node moves or becomes disabled or changes its visibility * Propagates the changes to connected elements, i.e. edges, numbers, etc. * Checks if the node is inside the scene. * @param change * @param value * @return */ QVariant Node::itemChange(GraphicsItemChange change, const QVariant &value) { switch (change) { case ItemPositionHasChanged : { //setCacheMode( QGraphicsItem::ItemCoordinateCache ); foreach (Edge *edge, inEdgeList) //Move each inEdge of this node edge->adjust(); foreach (Edge *edge, outEdgeList) //Move each outEdge of this node edge->adjust(); //Move its graphic number if ( m_hasNumber ) { if (!m_hasNumberInside) { //move it outside m_number -> setZValue(ZValueNodeNumber); m_number -> setPos( m_size+m_numberDistance, 0); } } if (m_hasLabel) { m_label->setPos( -m_size, m_labelDistance+m_size); } break; } case ItemEnabledHasChanged:{ if (ItemEnabledHasChanged) { return 1; } else{ return 0; } } case ItemVisibleHasChanged: { if (ItemVisibleHasChanged){ return 1; } else{ return 0; } } default: { break; } }; return QGraphicsItem::itemChange(change, value); } /** handles the events of a click on a node */ void Node::mousePressEvent(QGraphicsSceneMouseEvent *event) { QGraphicsItem::mousePressEvent(event); } void Node::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { update(); QGraphicsItem::mouseReleaseEvent(event); } /** * @brief Node::hoverEnterEvent * on hover on node, it highlights all connected edges * @param event */ void Node::hoverEnterEvent(QGraphicsSceneHoverEvent *event) { foreach (Edge *edge, inEdgeList) edge->highlight(true); foreach (Edge *edge, outEdgeList) edge->highlight(true); QGraphicsItem::hoverEnterEvent(event); } /** * @brief Node::hoverLeaveEvent * Stops the connected edges highlighting * @param event */ void Node::hoverLeaveEvent(QGraphicsSceneHoverEvent *event){ foreach (Edge *edge, inEdgeList) edge->highlight(false); foreach (Edge *edge, outEdgeList) edge->highlight(false); QGraphicsItem::hoverLeaveEvent(event); } /** * @brief Node::addInLink * Called from a new connected in-link to acknowloedge itself to this node. * @param edge */ void Node::addInLink( Edge *edge ) { qDebug() << "Node: addInLink() for "<< m_num; inEdgeList.push_back( edge); //qDebug ("Node: %i inEdgeList has now %i edges", m_num, inEdgeList.size()); } void Node::deleteInLink( Edge *link ){ qDebug () << "Node::deleteInLink() - to " << m_num << " inEdgeList size: " << inEdgeList.size(); inEdgeList.remove( link ); qDebug () << "Node::deleteInLink() - deleted to " << m_num << " inEdgeList size: " << inEdgeList.size(); } void Node::addOutLink( Edge *edge ) { qDebug("Node: addOutLink()"); outEdgeList.push_back( edge); // qDebug ("Node: outEdgeList has now %i edges", outEdgeList.size()); } void Node::deleteOutLink(Edge *link){ qDebug () << "Node::deleteOutLink() - from " << m_num << " outEdgeList size: " << outEdgeList.size(); outEdgeList.remove( link); qDebug () << "Node::deleteOutLink() - deleted from " << m_num << " outEdgeList size now: " << outEdgeList.size(); } void Node::addLabel () { qDebug()<< "Node::addLabel()" ; m_label = new NodeLabel (this, m_labelText, m_labelSize); m_label -> setDefaultTextColor (m_labelColor); m_label -> setPos( m_size, m_labelDistance+m_size); m_hasLabel = true; } NodeLabel* Node::label(){ if (!m_hasLabel) { addLabel(); } return m_label; } void Node::deleteLabel(){ qDebug ("Node: deleteLabel "); if (m_hasLabel) { m_hasLabel=false; m_label->hide(); graphicsWidget->removeItem(m_label); } qDebug () << "Node::deleteLabel() - finished"; } void Node::setLabelText ( QString label) { qDebug()<< "Node::setLabelText()"; prepareGeometryChange(); m_labelText = label; if (m_hasLabel) m_label->setPlainText(label); else addLabel(); m_hasLabel=true; } void Node::setLabelColor ( const QString &color) { qDebug()<< "Node::setLabelColor()"; prepareGeometryChange(); m_labelColor= color; if (m_hasLabel) m_label->setDefaultTextColor(color); else addLabel(); m_hasLabel=true; } void Node::setLabelVisibility(const bool &toggle) { if (toggle){ if (m_hasLabel) { m_label->show(); } else { addLabel(); } } else { if (m_hasLabel) { m_label->hide(); } } m_hasLabel=toggle; } void Node::setLabelSize(const int &size) { m_labelSize = size; if (!m_hasLabel) { addLabel(); } m_label->setSize(m_labelSize); } /** * @brief Node::labelText * @return QString */ QString Node::labelText ( ) { return m_labelText; } /** * @brief Node::setLabelDistance * @param distance */ void Node::setLabelDistance(const int &distance) { m_labelDistance = distance; if (!m_hasLabel) { addLabel(); } m_label->setPos( -m_size, m_size+m_labelDistance);; } void Node::addNumber () { qDebug()<<"Node::addNumber () " ; m_hasNumber=true; m_hasNumberInside = false; m_number= new NodeNumber ( this, QString::number(m_num), m_numSize); m_number -> setDefaultTextColor (m_numColor); m_number -> setPos(m_size+m_numberDistance, 0); } NodeNumber* Node::number(){ return m_number; } void Node::deleteNumber( ){ qDebug () << "Node::deleteNumber()"; if (m_hasNumber && !m_hasNumberInside) { m_number->hide(); graphicsWidget->removeItem(m_number); m_hasNumber=false; } qDebug () << "Node::deleteNumber() - finished"; } void Node::setNumberVisibility(const bool &toggle) { qDebug() << "Node::setNumberVisibility() " << toggle; if (toggle) { //show if (!m_hasNumber) { m_hasNumber=toggle; if ( !m_hasNumberInside ) addNumber(); else { setShape(m_shape); } } } else { // hide deleteNumber(); m_hasNumber=toggle; setShape(m_shape); } } void Node::setNumberInside (const bool &toggle){ qDebug()<<"Node::setNumberInside() " << toggle; if (toggle) { // set number inside deleteNumber(); } else { addNumber(); } m_hasNumber = true; m_hasNumberInside = toggle; setShape(m_shape); } /** * @brief Node::setNumberSize * @param size */ void Node::setNumberSize(const int &size) { m_numSize = size; if (m_hasNumber && !m_hasNumberInside) { m_number->setSize(m_numSize); } else if (m_hasNumber && m_hasNumberInside) { setShape(m_shape); } else { // create a nodeNumber ? } } /** * @brief Node::setNumberColor * @param color */ void Node::setNumberColor(const QString &color) { m_numColor = color; if (m_hasNumber){ if (m_hasNumberInside) { setShape(m_shape); } else { m_number -> setDefaultTextColor (m_numColor); } } } /** * @brief Node::setNumberDistance * @param distance */ void Node::setNumberDistance(const int &distance) { m_numberDistance = distance; if (m_hasNumber && !m_hasNumberInside) { m_number -> setPos( m_size+m_numberDistance, 0); } else if (m_hasNumber && m_hasNumberInside) { // do nothing } else { // create a nodeNumber ? } } Node::~Node(){ qDebug() << "*** ~Node() - node "<< nodeNumber() << "inEdgeList.size = " << inEdgeList.size() << "outEdgeList.size = " << outEdgeList.size(); foreach (Edge *edge, inEdgeList) { qDebug("~Node: removing edges in inEdgeList"); delete edge; } foreach (Edge *edge, outEdgeList) { qDebug("~Node: removing edges in outEdgeList"); delete edge; } if ( m_hasNumber ) deleteNumber(); if ( m_hasLabel ) deleteLabel(); inEdgeList.clear(); outEdgeList.clear(); this->hide(); graphicsWidget->removeItem(this); } socnetv-2.2/src/node.h000755 000765 000024 00000011616 13040734202 015715 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt node.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef NODE_H #define NODE_H #include #include #include class GraphicsWidget; class QGraphicsSceneMouseEvent; class Edge; class NodeLabel; class NodeNumber; using namespace std; static const int TypeNode = QGraphicsItem::UserType+1; static const int ZValueNode = 100; static const int ZValueNodeHighlighted = 110; /** * This is actually a container-class. * Contains the graphical objects called Nodes, * which are displayed as triangles, boxes, circles, etc, on the canvas. * Each node "knows" the others with which she is connected. */ // class Node : public QObject, public QGraphicsItem { Q_OBJECT Q_INTERFACES (QGraphicsItem) public: Node (GraphicsWidget* gw, const int &num, const int &size, const QString &color, const QString &shape, const bool &showNumbers, const bool &numbersInside, const QString &numberColor, const int &numberSize, const int &numDistance, const bool &showLabels, const QString &label, const QString &labelColor, const int &labelSize, const int &labelDistance, QPointF p ); ~Node(); enum { Type = UserType + 1 }; int type() const { return Type; } QRectF boundingRect() const; QPainterPath shape() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); long int nodeNumber() {return m_num;} void addInLink( Edge *edge ) ; void deleteInLink(Edge*); void addOutLink( Edge *edge ) ; void deleteOutLink(Edge*); void setSize(const int &); int size() const; void setShape (const QString); QString nodeShape() {return m_shape;} void setColor(QString str); void setColor(QColor color); QString color (); void addLabel(); NodeLabel* label(); void deleteLabel(); void setLabelVisibility(const bool &toggle); void setLabelSize(const int &size); void setLabelText ( QString label) ; void setLabelColor (const QString &color) ; QString labelText(); void setLabelDistance(const int &distance); void addNumber () ; NodeNumber* number(); void deleteNumber(); void setNumberVisibility(const bool &toggle); void setNumberInside(const bool &toggle); void setNumberSize(const int &size); void setNumberDistance(const int &distance); void setNumberColor(const QString &color); void toggleAntialiasing(bool); protected: QVariant itemChange(GraphicsItemChange change, const QVariant &value); void mousePressEvent(QGraphicsSceneMouseEvent *event); void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); void hoverEnterEvent(QGraphicsSceneHoverEvent *event); void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); signals: void nodeClicked(Node*); void startEdge(Node *); void adjustOutEdge(); void adjustInEdge(); void removeOutEdge(); void removeInEdge(); private: GraphicsWidget *graphicsWidget; QPainterPath *m_path; QPointF newPos; QPolygonF *m_poly_t; int m_size, m_numSize, m_labelSize, m_numberDistance, m_labelDistance; long int m_num; QString m_shape, m_col_str, m_numColor, m_labelText, m_labelColor; QColor m_col; bool m_hasNumber, m_hasLabel, m_hasNumberInside; /**Lists of elements attached to this node */ list inEdgeList, outEdgeList; NodeLabel *m_label; NodeNumber *m_number; }; #endif socnetv-2.2/src/nodelabel.cpp000755 000765 000024 00000004260 13040734201 017244 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt nodelabel.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include "nodelabel.h" #include "node.h" #include NodeLabel::NodeLabel(Node *jim , const QString &text, const int &size) : QGraphicsTextItem(jim) { source=jim; setParentItem(jim); //auto disables child items like this, when node is disabled. setPlainText( text ); setFont( QFont ("Times", size, QFont::Light, true) ); setZValue(ZValueNodeLabel); setAcceptHoverEvents(false); } void NodeLabel::setSize(const int &size) { prepareGeometryChange(); setFont( QFont ("Times", size, QFont::Black, false) ); //update(); } void NodeLabel::removeRefs(){ source->deleteLabel(); } NodeLabel::~NodeLabel(){ } socnetv-2.2/src/nodelabel.h000755 000765 000024 00000004031 13040734202 016706 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt nodelabel.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef NODELABEL_H #define NODELABEL_H #include class Node; static const int TypeLabel = QGraphicsItem::UserType+4; static const int ZValueNodeLabel = 80; class NodeLabel : public QGraphicsTextItem{ public: NodeLabel(Node * , const QString &text, const int &size ); void removeRefs(); enum { Type = UserType + 4 }; int type() const { return Type; } void setSize(const int &size); ~NodeLabel(); Node* node() { return source; } private: Node *source; }; #endif socnetv-2.2/src/nodenumber.cpp000755 000765 000024 00000004322 13040734201 017454 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt nodenumber.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include "nodenumber.h" #include "node.h" #include NodeNumber::NodeNumber( Node *jim , const QString &labelText, const int &size) :QGraphicsTextItem(jim) { source=jim; setParentItem(jim); //auto disables child items like this, when node is disabled. setPlainText( labelText ); setFont( QFont ("Times", size, QFont::Black, false) ); setZValue(ZValueNodeNumber); setAcceptHoverEvents(false); } void NodeNumber::setSize(const int size) { prepareGeometryChange(); setFont( QFont ("Times", size, QFont::Black, false) ); //update(); } void NodeNumber::removeRefs(){ source->deleteNumber(); } NodeNumber::~NodeNumber(){ } socnetv-2.2/src/nodenumber.h000755 000765 000024 00000004043 13040734202 017122 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt nodenumber.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef NODENUMBER_H #define NODENUMBER_H #include class Node; static const int TypeNumber=QGraphicsItem::UserType+3; static const int ZValueNodeNumber = 90; class NodeNumber : public QGraphicsTextItem { public: NodeNumber(Node * , const QString &labelText, const int &size); enum { Type = UserType + 3 }; void removeRefs(); int type() const { return Type; } Node* node() { return source; } void setSize(const int size); ~NodeNumber(); private: Node *source; }; #endif socnetv-2.2/src/parser.cpp000755 000765 000024 00000436560 13040734201 016627 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt parser.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include "parser.h" #include #include #include #include #include #include #include //used for qDebug messages #include #include #include #include #include // used as list listDummiesPajek #include //for priority queue #include "graph.h" //needed for setParent using namespace std; Parser::Parser(const QString fn, const QString m_codec, const int iNS, const QString iNC, const QString iNSh, const QString iNNC, const int iNNS, const QString iNLC, const int iNLS , const QString iEC, const int width, const int height, const int fFormat, const int sm_mode, const QString delim) { qDebug() << "Parser::Parser() fn: " << fn << "running on thread " << this->thread() ; initNodeSize=iNS; initNodeColor=iNC; initNodeShape=iNSh; initNodeNumberColor=iNNC; initNodeNumberSize=iNNS; initNodeLabelColor=iNLC; initNodeLabelSize=iNLS; initEdgeColor=iEC; edgeDirType=EDGE_DIRECTED; arrows=true; bezier=false; fileName=fn; userSelectedCodecName = m_codec; networkName=(fileName.split ("/")).last(); gwWidth=width; gwHeight=height; randX=0; randY=0; fileFormat= fFormat; two_sm_mode = sm_mode; if (!delim.isNull() && !delim.isEmpty()) delimiter = delim; else delimiter=" "; qDebug() << "Parser::Parser() - delim" << delim << "delimiter"<thread() <<" clearing hashes... "; nodeHash.clear(); keyFor.clear(); keyName.clear(); keyType.clear(); keyDefaultValue.clear(); edgesMissingNodesHash.clear(); edgeMissingNodesList.clear(); edgeMissingNodesListData.clear(); firstModeMultiMap.clear(); secondModeMultiMap.clear(); if (xml!=0) { qDebug()<< "**** Parser::~Parser() clearing xml reader object " ; xml->clear(); delete xml; xml=0; } } /** starts the new thread calling the load* methods */ void Parser::run() { qDebug()<< "**** Parser::run() - On a new thread " << this->thread() << " networkName "<< networkName << " fileFormat "<< fileFormat ; errorMessage=QString::null; switch (fileFormat){ case FILE_GRAPHML: qDebug()<< "Parser::run() - calling loadGraphML()"; if (loadGraphML()){ qDebug()<< "Parser::run() - that was GRAPHML-formatted file"; } break; case FILE_PAJEK: qDebug()<< "Parser::run() - calling loadPajek()"; if ( loadPajek() ) { qDebug()<< "Parser::run() - that was PAJEK formatted file"; } break; case FILE_ADJACENCY: qDebug()<< "Parser::run() - calling loadAdjacency()"; if (loadAdjacency() ) { qDebug()<< "Parser::run() - that was ADJACENCY-formatted file"; } break; case FILE_GRAPHVIZ: qDebug()<< "Parser::run() - calling loadDot()"; if (loadDot() ) { qDebug()<< "Parser::run() - that was GRAPHVIZ-formatted file"; } break; case FILE_UCINET: qDebug()<< "Parser::run() - calling loadDL()"; if (loadDL() ){ qDebug()<< "Parser::run() - that was UCINET-formatted file"; } break; case FILE_GML: qDebug()<< "Parser::run() - calling loadGML()"; if (loadGML() ){ qDebug()<< "Parser::run() - that was GML-formatted file"; } break; case FILE_EDGELIST_WEIGHTED: qDebug()<< "Parser::run() - calling loadEdgeListWeighted()"; if (loadEdgeListWeighed(delimiter) ){ qDebug()<< "Parser::run() - that was weighted EDGELIST-formatted file"; } break; case FILE_EDGELIST_SIMPLE: qDebug()<< "Parser::run() - calling loadEdgeListSimple()"; if (loadEdgeListSimple(delimiter) ){ qDebug()<< "Parser::run() - that was simple EDGELIST-formatted file"; } break; case FILE_TWOMODE: qDebug()<< "Parser::run() - calling loadTwoModeSociomatrix()"; if (loadTwoModeSociomatrix() ){ qDebug()<< "Parser::run() - that was weigted TWOMODE-formatted file"; } break; default: //GraphML qDebug()<< "Parser::run() - default case - calling loadGraphML()"; if (loadGraphML() ){ qDebug()<< "Parser::run() - that was GRAPHML-formatted file"; } break; } if (errorMessage!=QString::null) { loadFileError(errorMessage); return; } qDebug()<< "**** Parser::run() - on thread " << this->thread() << "Reached end. " "Emitting finished() calling loadFileError() if any" << " fileFormat now "<< fileFormat ; emit finished ("Parser::run() - reach end"); } /** * @brief Parser::loadFileError * @param errorMessage * Called when some Parser method cannot read or parse correctly a file. * It informs the Graph and then MW with an error message */ void Parser::loadFileError(const QString &errorMessage) { qDebug()<<"Parser::loadFileError() - errorMessage:" <0){ qDebug() << "Parser::loadDL() - we are at source 1. " "Checking relationList"; relation = relationsList[ relationCounter ]; qDebug() << "Parser::loadDL() - " "WE ARE THE FIRST DATASET/MATRIX" << " source node counter is " << source << " and relation to " << relation<< ": " << relationCounter; emit relationSet (relationCounter); } else if (source>totalNodes) { source=1; relationCounter++; relation = relationsList[ relationCounter ]; qDebug() << "Parser::loadDL() - " "LOOKS LIKE WE ENTERED A NEW DATASET/MATRIX " << " init source node counter to " << source << " and relation to " << relation << ": " << relationCounter; emit relationSet (relationCounter); } else { qDebug() << "Parser::loadDL() - source node counter is " << source; } for (QStringList::Iterator it1 = lineElement.begin(); it1!=lineElement.end(); ++it1) { //qDebug()<< (*it1).toLatin1() ; if ( (*it1)!="0"){ edgeWeight=(*it1).toFloat(&floatOK); qDebug() << "Parser::loadDL() - relation " << relationCounter << " found edge from " << source << " to " << target << " weight " << edgeWeight << " emitting edgeCreate() to parent" ; emit edgeCreate( source, target, edgeWeight, initEdgeColor, EDGE_DIRECTED, arrows, bezier); totalLinks++; qDebug() << "Parser::loadDL() - TotalLinks= " << totalLinks; } target++; } source++; } if (data_flag && edgelist1Format) { //read edges in edgelist1 format // check if we haven't created any nodes... if ( nodeSum < totalNodes ){ qDebug() << "Parser::loadDL() - nodes have not been created yet. " << " calling createRandomNodes()" ; createRandomNodes(1, QString::null, totalNodes); nodeSum = totalNodes; } lineElement=str.split(QRegExp("\\s+"), QString::SkipEmptyParts); if ( lineElement.count() != 3 ) { qDebug() << "Parser::loadDL() - Not an edgelist1 UCINET " "formatted file. Aborting!!"; file.close(); //emit something... errorMessage = tr("UCINET file declared as edgelist but I found " "a line which did not have 3 elements (source, target, weight)"); return false; } source = (lineElement[0]).toInt(&intOK); target = (lineElement[1]).toInt(&intOK); qDebug() << "Parser::loadDL() - source node " << source << " target node " << target; edgeWeight=(lineElement[2]).toDouble(&intOK); if (intOK) { qDebug() << "Parser::loadDL() -list file declares edge weight: " << edgeWeight; } else { edgeWeight=1.0; qDebug () << " list file NOT declaring edge weight. Setting default: " << edgeWeight; } qDebug() << "Parser::loadDL() - Creating link " << source << " -> "<< target << " weight= "<< edgeWeight << " TotalLinks= " << totalLinks+1; emit edgeCreate(source, target, edgeWeight, initEdgeColor, EDGE_DIRECTED, arrows, bezier); totalLinks++; } } //sanity check if (nodeSum != totalNodes) { qDebug()<< "Error: aborting"; //emit something errorMessage = tr("UCINET declared N actors initially, " "but I found a different number of actors"); return false; } if (relationsList.count() == 0) { emit addRelation("unnamed"); } //The network has been loaded. Tell MW the statistics and network type emit relationSet (0); qDebug() << "Parser::loadDL() - FINISHED - " "emitting networkFileLoaded() and clearing arrays"; emit networkFileLoaded(FILE_UCINET, fileName, networkName, totalNodes, totalLinks, EDGE_DIRECTED); lineElement.clear(); labelsList.clear(); relationsList.clear(); return true; } /** Tries to load the file as Pajek-formatted network. If not it returns -1 */ bool Parser::loadPajek(){ qDebug ("\n\nParser: loadPajek"); QFile file ( fileName ); if ( ! file.open(QIODevice::ReadOnly )) { errorMessage = tr("Cannot open Pajek file"); return false; } QTextStream ts( &file ); ts.setCodec(userSelectedCodecName.toUtf8()); QString str, label, temp; nodeColor=""; edgeColor=""; nodeShape=""; initEdgeLabel = QString::null; QStringList lineElement; bool ok=false, intOk=false, check1=false, check2=false; bool has_arcs=false; bool nodes_flag=false, edges_flag=false, arcs_flag=false, arcslist_flag=false, matrix_flag=false; fileContainsNodeColors=false; fileContainsNodeCoords=false; fileContainsLinkColors=false; fileContainsLinkLabels=false; bool zero_flag=false; int i=0, j=0, miss=0, source= -1, target=-1, nodeNum, colorIndex=-1; int coordIndex=-1, labelIndex=-1; unsigned long int lineCounter=0; int pos=-1, relationCounter=0; float weight=1; QString relation; list listDummiesPajek; totalLinks=0; totalNodes=0; j=0; //counts how many real nodes exist in the file miss=0; //counts missing nodeNumbers. //if j + miss < nodeNum, it creates (nodeNum-miss) dummy nodes which are deleted in the end. relationsList.clear(); while ( !ts.atEnd() ) { str= ts.readLine(); str = str.simplified(); if ( isComment(str) ) continue; lineCounter++; qDebug()<< "*** Parser:loadPajek(): " << str; if (lineCounter==1) { if ( str.startsWith("graph",Qt::CaseInsensitive) || str.startsWith("digraph",Qt::CaseInsensitive) || str.startsWith("DL",Qt::CaseInsensitive) || str.startsWith("list",Qt::CaseInsensitive) || str.startsWith("graphml",Qt::CaseInsensitive) || str.startsWith(" 0) { qDebug () << "Parser::loadPajek() relationCounter " << relationCounter << "emitting relationSet"; emit relationSet(relationCounter); i=0; // reset the source node index } relationCounter++; } continue; } else if ( str.contains( "*arcslist", Qt::CaseInsensitive) ) { arcs_flag=false; edges_flag=false; arcslist_flag=true; matrix_flag=false; continue; } else if ( str.contains( "*matrix", Qt::CaseInsensitive) ) { qDebug() << str ; arcs_flag=false; edges_flag=false; arcslist_flag=false; matrix_flag=true; //check if row has label for matrix data, // and use it as relation name if ( (pos = str.indexOf(":")) != -1 ) { relation = str.right(str.size() - pos -1) ; relation = relation.simplified(); qDebug() << "Parser::loadPajek() - adding relation "<< relation << " to relationsList and emitting addRelation "; relationsList << relation; emit addRelation( relation ); if (relationCounter > 0) { qDebug () << "Parser::loadPajek() relationCounter " << relationCounter << "emitting relationSet"; emit relationSet(relationCounter); i=0; // reset the source node index } relationCounter++; } continue; } /** READING NODES */ if (!edges_flag && !arcs_flag && !arcslist_flag && !matrix_flag) { //qDebug("=== Reading nodes ==="); nodes_flag=true; nodeNum=lineElement[0].toInt(&intOk, 10); //qDebug()<<"node number: "< nodeNum ) { qDebug ("Error: This Pajek net declares this node with nodeNumber smaller than previous nodes. Aborting"); errorMessage = tr("Pajek-formatted file declares a node with " "nodeNumber smaller than previous nodes."); return false; } emit createNode( nodeNum,initNodeSize, nodeColor, initNodeNumberColor, initNodeNumberSize, label, initNodeLabelColor, initNodeLabelSize, QPointF(randX, randY), nodeShape, false ); initNodeColor=nodeColor; } // NODES CREATED. CREATE EDGES/ARCS NOW. // first check that all nodes are already created else { if (j && j!=totalNodes) { //if there were more or less nodes than the file declared qDebug()<<"*** WARNING ***: The Pajek file declares " << totalNodes <<" nodes, but I found " << j << " nodes...." ; totalNodes=j; } else if (j==0) { //if there were no nodes at all, we need to create them now. qDebug()<< "The Pajek file declares "<< totalNodes<< " but I didnt found any nodes. I will create them...."; for (int num=j+1; num<= totalNodes; num++) { qDebug() << "Parser-loadPajek(): Creating node number i = "<< num; randX=rand()%gwWidth; randY=rand()%gwHeight; emit createNode( num,initNodeSize, initNodeColor, initNodeNumberColor, initNodeNumberSize, QString::number(i), initNodeLabelColor,initNodeLabelSize, QPointF(randX, randY), initNodeShape, false ); } j=totalNodes; } if (edges_flag && !arcs_flag) { /**EDGES */ qDebug("Parser-loadPajek(): ==== Reading edges ===="); qDebug()< 0."); return false; // i --> (i-1) internally } else if (source < 0 && target >0 ) { //weights come first... edgeWeight = lineElement[0].toFloat(&ok); source= lineElement[1].toInt(&ok, 10); if (lineElement.count()>2) { target = lineElement[2].toInt(&ok,10); } else { target = lineElement[1].toInt(&ok,10); //self link } } else if (lineElement.count()>2) edgeWeight =lineElement[2].toFloat(&ok); else edgeWeight =1.0; //qDebug()<<"Parser-loadPajek(): weight "<< weight; if (lineElement.contains("c", Qt::CaseSensitive ) ) { //qDebug("Parser-loadPajek(): file with link colours"); fileContainsLinkColors=true; colorIndex=lineElement.indexOf( QRegExp("[c]"), 0 ) + 1; if (colorIndex >= lineElement.count()) edgeColor=initEdgeColor; else edgeColor=lineElement [ colorIndex ]; if (edgeColor.contains (".") ) edgeColor=initEdgeColor; //qDebug()<< " current color "<< edgeColor; } else { //qDebug("Parser-loadPajek(): file with no link colours"); edgeColor=initEdgeColor; } if (lineElement.contains("l", Qt::CaseSensitive ) ) { qDebug("Parser-loadPajek(): file with link labels"); fileContainsLinkLabels=true; labelIndex=lineElement.indexOf( QRegExp("[l]"), 0 ) + 1; if (labelIndex >= lineElement.count()) edgeLabel=initEdgeLabel; else edgeLabel=lineElement [ labelIndex ]; if (edgeLabel.contains (".") ) edgeLabel=initEdgeLabel; qDebug()<< " edge label "<< edgeLabel; } else { //qDebug("Parser-loadPajek(): file with no link labels"); edgeLabel=initEdgeLabel; } arrows=false; bezier=false; qDebug()<< "Parser-loadPajek(): EDGES: Create edge between " << source << " - "<< target; emit edgeCreate(source, target, edgeWeight, edgeColor, EDGE_RECIPROCAL_UNDIRECTED, arrows, bezier, edgeLabel); totalLinks=totalLinks+2; } //end if EDGES else if (!edges_flag && arcs_flag) { /** ARCS */ //qDebug("Parser-loadPajek(): === Reading arcs ==="); source= lineElement[0].toInt(&ok, 10); target = lineElement[1].toInt(&ok,10); if (source == 0 || target == 0 ) { errorMessage = tr("Pajek-formatted file declares arc " "with a zero source or target nodeNumber. " "Each node should have a nodeNumber > 0."); return false; // i --> (i-1) internally } else if (source < 0 && target >0 ) { //weights come first... edgeWeight = lineElement[0].toFloat(&ok); source= lineElement[1].toInt(&ok, 10); if (lineElement.count()>2) { target = lineElement[2].toInt(&ok,10); } else { target = lineElement[1].toInt(&ok,10); //self link } } else if (lineElement.count()>2) edgeWeight =lineElement[2].toFloat(&ok); else edgeWeight =1.0; if (lineElement.contains("c", Qt::CaseSensitive ) ) { //qDebug("Parser-loadPajek(): file with link colours"); edgeColor=lineElement.at ( lineElement.indexOf( QRegExp("[c]"), 0 ) + 1 ); fileContainsLinkColors=true; } else { //qDebug("Parser-loadPajek(): file with no link colours"); edgeColor=initEdgeColor; } if (lineElement.contains("l", Qt::CaseSensitive ) ) { qDebug("Parser-loadPajek(): file with link labels"); fileContainsLinkLabels=true; labelIndex=lineElement.indexOf( QRegExp("[l]"), 0 ) + 1; if (labelIndex >= lineElement.count()) edgeLabel=initEdgeLabel; else edgeLabel=lineElement.at ( labelIndex ); //if (edgeLabel.contains (".") ) edgeLabel=initEdgeLabel; qDebug()<< " edge label "<< edgeLabel; } else { //qDebug("Parser-loadPajek(): file with no link labels"); edgeLabel=initEdgeLabel; } arrows=true; bezier=false; has_arcs=true; qDebug()<<"Parser-loadPajek(): ARCS: Creating arc from node "<< source << " to node "<< target << " with weight "<< weight; emit edgeCreate(source, target, edgeWeight , edgeColor, EDGE_DIRECTED, arrows, bezier, edgeLabel); totalLinks++; } //else if ARCS else if (arcslist_flag) { /** ARCSlist */ //qDebug("Parser-loadPajek(): === Reading arcs list==="); if (lineElement[0].startsWith("-") ) lineElement[0].remove(0,1); source= lineElement[0].toInt(&ok, 10); fileContainsLinkColors=false; edgeColor=initEdgeColor; has_arcs=true; arrows=true; bezier=false; for (int index = 1; index < lineElement.size(); index++) { target = lineElement.at(index).toInt(&ok,10); qDebug()<<"Parser-loadPajek(): ARCS LIST: Creating ARC source "<< source << " target "<< target << " with weight "<< weight; emit edgeCreate(source, target, edgeWeight, edgeColor, EDGE_DIRECTED, arrows, bezier); totalLinks++; } } //else if ARCSLIST else if (matrix_flag) { /** matrix */ //qDebug("Parser-loadPajek(): === Reading matrix of edges==="); i++; source= i; fileContainsLinkColors=false; edgeColor=initEdgeColor; has_arcs=true; arrows=true; bezier=false; for (target = 0; target < lineElement.size(); target ++) { if ( lineElement.at(target) != "0" ) { edgeWeight = lineElement.at(target).toFloat(&ok); qDebug()<<"Parser-loadPajek(): MATRIX: Creating arc source " << source << " target "<< target +1 << " with weight "<< weight; emit edgeCreate(source, target+1, edgeWeight, edgeColor, EDGE_DIRECTED, arrows, bezier); totalLinks++; } } } //else if matrix } //end if BOTH ARCS AND EDGES } //end WHILE file.close(); if (j==0) { errorMessage = tr("Could not find node declarations in this Pajek-formatted file."); return false; } qDebug("Parser-loadPajek(): Removing all dummy nodes, if any"); if (listDummiesPajek.size() > 0 ) { qDebug("Trying to delete the dummies now"); for ( list::iterator it=listDummiesPajek.begin(); it!=listDummiesPajek.end(); it++ ) { emit removeDummyNode(*it); } } if (relationsList.count() == 0) { emit addRelation(networkName); } qDebug("Parser-loadPajek(): Clearing DumiesList from Pajek"); listDummiesPajek.clear(); relationsList.clear(); emit relationSet (0); //The network has been loaded. Tell MW the statistics and network type emit networkFileLoaded(FILE_PAJEK, fileName, networkName, totalNodes, totalLinks, ( (has_arcs) ? EDGE_DIRECTED: EDGE_RECIPROCAL_UNDIRECTED)); return true; } /** Tries to load the file as adjacency sociomatrix-formatted. If not it returns -1 */ bool Parser::loadAdjacency(){ qDebug("\n\nParser: loadAdjacency()"); QFile file ( fileName ); if ( ! file.open(QIODevice::ReadOnly )) { return false; } QTextStream ts( &file ); ts.setCodec(userSelectedCodecName.toUtf8()); QString str; QStringList lineElement; int i=0, j=0, newCount=0, lastCount=0; bool intOK=false; relationsList.clear(); totalNodes=0; edgeWeight=1.0; totalLinks=0; i=1; while ( i < 11 && !ts.atEnd() ) { str= ts.readLine() ; str=str.simplified(); if ( isComment(str) ) continue; if ( str.contains("vertices",Qt::CaseInsensitive) || str.contains("network",Qt::CaseInsensitive) || str.contains("graph",Qt::CaseInsensitive) || str.contains("digraph",Qt::CaseInsensitive) || str.contains("DL",Qt::CaseInsensitive) || str.contains("list",Qt::CaseInsensitive) || str.contains("graphml",Qt::CaseInsensitive) || str.contains("xml",Qt::CaseInsensitive) ) { qDebug()<< "*** Parser:loadAdjacency(): Not an Adjacency-formatted file. Aborting!!"; file.close(); errorMessage = tr("Not an Adjacency-formatted file. " "A non-comment line includes prohibited strings (i.e GraphML), " "not only numbers and delimiters as expected."); return false; } if ( str.contains (",")) newCount = (str.split(",")).count(); else newCount = (str.split(" ")).count(); qDebug() << str; qDebug() << "newCount "<1 ) || (newCount < i) ) { // line element count differ, therefore this can't be an adjacency matrix qDebug()<< "*** Parser:loadAdjacency(): Not an Adjacency-formatted file. Aborting!!"; file.close(); errorMessage = tr("Error reading Adjacency-formatted file. " "Matrix row %1 has different number of elements from previous row.").arg(i); return false; } lastCount=newCount; i++; } ts.reset(); ts.seek(0); i=0; while ( !ts.atEnd() ) { str= ts.readLine() ; str=str.simplified(); // transforms "/t", " ", etc to plain " ". if ( isComment(str) ) continue; if ( str.contains (",")) lineElement=str.split(","); else lineElement=str.split(" "); if (i == 0 ) { totalNodes=lineElement.count(); qDebug("Parser-loadAdjacency(): There are %i nodes in this file", totalNodes); for (j=0; jaddData(encodedData); qDebug() << " Parser::loadGraphML(): test if XML document encoding == userCodec"; xml->readNext(); if (xml->isStartDocument()) { qDebug()<< " Parser::loadGraphML(): Testing XML document " << " version " << xml->documentVersion() << " encoding " << xml->documentEncoding() << " userSelectedCodecName.toUtf8() " << userSelectedCodecName.toUtf8(); if ( xml->documentEncoding().toString() != userSelectedCodecName) { qDebug() << " Parser::loadGraphML(): Conflicting encodings. " << " Re-reading data with userCodec"; xml->clear(); QTextStream in(&encodedData); in.setAutoDetectUnicode(false); QTextCodec *codec = QTextCodec::codecForName( userSelectedCodec ); in.setCodec(codec); QString decodedData = in.readAll(); xml->addData(decodedData); } else { qDebug() << " Parser::loadGraphML(): Testing XML: OK"; xml->clear(); xml->addData(encodedData); } } while (!xml->atEnd()) { xml->readNext(); qDebug()<< " Parser::loadGraphML(): xml->token "<< xml->tokenString(); if (xml->isStartDocument()) { qDebug()<< " Parser::loadGraphML(): xml startDocument" << " version " << xml->documentVersion() << " encoding " << xml->documentEncoding(); } if (xml->isStartElement()) { qDebug()<< " Parser::loadGraphML(): element name "<< xml->name().toString(); if (xml->name() == "graphml") { qDebug()<< " Parser::loadGraphML(): GraphML start. NamespaceUri is " << xml->namespaceUri().toString() << "Calling readGraphML()"; if (! readGraphML(*xml) ) { return false; } } else { //not a GraphML doc, return false. xml->raiseError( QObject::tr(" loadGraphML(): not a GraphML file.")); qDebug()<< "*** loadGraphML(): Error in startElement " << " The file is not an GraphML version 1.0 file "; file.close(); errorMessage = tr("XML at startElement but element name not graphml."); return false; } } else if ( xml->tokenString() == "Invalid" ){ xml->raiseError( QObject::tr(" loadGraphML(): invalid GraphML or encoding.")); qDebug()<< "*** loadGraphML(): Cannot find startElement" << " The file is not valid GraphML or has invalid encoding"; file.close(); errorMessage = tr("XML tokenString at line %1 invalid.").arg(xml->lineNumber()); return false; } } emit relationSet (0); //The network has been loaded. Tell MW the statistics and network type emit networkFileLoaded(FILE_GRAPHML, fileName, networkName, totalNodes, totalLinks, edgeDirType); //clear our mess - remove every hash element... keyFor.clear(); keyName.clear(); keyType.clear(); keyDefaultValue.clear(); nodeHash.clear(); return true; } /* * Called from loadGraphML * This method checks the xml token name and calls the appropriate function. */ bool Parser::readGraphML(QXmlStreamReader &xml){ qDebug()<< " Parser::readGraphML() " ; bool_node=false; bool_edge=false; bool_key=false; //Q_ASSERT(xml.isStartElement() && xml.name() == "graph"); while (!xml.atEnd()) { //start reading until QXmlStreamReader end(). xml.readNext(); //read next token qDebug()<< "Parser::readGraphML() - line:" << xml.lineNumber(); if (xml.hasError()) { qDebug()<< "Parser::readGraphML() - xml.hasError():" << xml.errorString(); errorMessage = tr("XML has error at line %1, token name %2:\n%3") .arg(xml.lineNumber()) .arg(xml.name().toString()) .arg(xml.errorString()); return false; } if (xml.isStartElement()) { //new token (graph, node, or edge) here qDebug()<< "Parser::readGraphML() - start of element: " << xml.name().toString() ; if (xml.name() == "graph") //graph definition token readGraphMLElementGraph(xml); else if (xml.name() == "key") {//key definition token QXmlStreamAttributes xmlStreamAttr = xml.attributes(); readGraphMLElementKey( xmlStreamAttr ); } else if (xml.name() == "default") //default key value token readGraphMLElementDefaultValue(xml); else if (xml.name() == "node") //graph definition token readGraphMLElementNode(xml); else if (xml.name() == "data") //data definition token readGraphMLElementData(xml); else if ( xml.name() == "ShapeNode") { bool_node = true; } else if ( ( xml.name() == "Geometry" || xml.name() == "Fill" || xml.name() == "BorderStyle" || xml.name() == "NodeLabel" || xml.name() == "Shape" ) && bool_node ) { readGraphMLElementNodeGraphics(xml); } else if (xml.name() == "edge") {//edge definition token QXmlStreamAttributes xmlStreamAttr = xml.attributes(); readGraphMLElementEdge( xmlStreamAttr ); } else if ( xml.name() == "BezierEdge") { bool_edge = true; } else if ( ( xml.name() == "Path" || xml.name() == "LineStyle" || xml.name() == "Arrows" || xml.name() == "EdgeLabel" ) && bool_edge ) { readGraphMLElementEdgeGraphics(xml); } else readGraphMLElementUnknown(xml); } if (xml.isEndElement()) { //token ends here qDebug()<< "Parser::readGraphML() - element ends here: " << xml.name().toString() ; if (xml.name() == "node") //node definition end endGraphMLElementNode(xml); else if (xml.name() == "edge") //edge definition end endGraphMLElementEdge(xml); } } // call createMissingNodeEdges() to create any edges with missing nodes createMissingNodeEdges(); return true; } // this method reads a graph definition // called at Graph element void Parser::readGraphMLElementGraph(QXmlStreamReader &xml){ qDebug()<< "Parser::readGraphMLElementGraph"; QXmlStreamAttributes xmlStreamAttr = xml.attributes(); QString defaultDirection = xmlStreamAttr.value("edgedefault").toString(); qDebug()<< "Parser::readGraphMLElementGraph() - edgedefault " << defaultDirection; if (defaultDirection=="undirected"){ qDebug()<< "Parser::readGraphMLElementGraph() - this is an undirected graph "; edgeDirType=EDGE_RECIPROCAL_UNDIRECTED; arrows=false; } else { qDebug()<< "Parser::readGraphMLElementGraph() - this is a directed graph "; edgeDirType=EDGE_DIRECTED; arrows=true; } networkName = xmlStreamAttr.value("id").toString(); relationsList << networkName; qDebug()<< "Parser::readGraphMLElementGraph() - emit addRelation()" < 0) { totalNodes=0; qDebug () << "Parser::readGraphMLElementGraph() - relations now " << relationCounter << "emitting relationSet to change to the new relation" << "and setting totalNodes to " < 1 ) { qDebug()<<"Parser::endGraphMLElementNode() - multirelational data" "skipping node creation. Node should have been created in earlier relation"; bool_node = false; return; } qDebug()<<"Parser::endGraphMLElementNode() - signal to create node " << " nodenumber "<< totalNodes << " id " << node_id << " label " << nodeLabel << " coords " <"+edge_target, QString::number(edgeWeight)+"|"+edgeColor +"|"+QString::number(edgeDirType)); missingNode=true; } if (!nodeHash.contains(edge_target)) { qDebug() << "Parser::readGraphMLElementEdge() - target node id " << edge_target << "for edge from " << edge_source << " to " << edge_target << " DOES NOT EXIST!" << "Inserting into edgesMissingNodesHash"; edgesMissingNodesHash.insert(edge_source+"===>"+edge_target, QString::number(edgeWeight)+"|"+edgeColor +"|"+QString::number(edgeDirType)); missingNode=true; } if (missingNode) { return; } source = nodeHash [edge_source]; target = nodeHash [edge_target]; qDebug()<< "Parser::readGraphMLElementEdge() - source "<< edge_source << " num "<< source <<" - target "<< edge_target << " num "<< target << " edgeDirType " << edgeDirType; } // this method emits the edge creation signal. // called at the end of edge element void Parser::endGraphMLElementEdge(QXmlStreamReader &xml){ Q_UNUSED(xml); if (missingNode) { qDebug()<<"Parser::endGraphMLElementEdge() - missingNode true " << " postponing edge creation signal"; return; } qDebug()<<"Parser::endGraphMLElementEdge() - signal edgeCreate " << source << " -> " << target << " edgeDirType value " << edgeDirType; emit edgeCreate(source, target, edgeWeight, edgeColor, edgeDirType, arrows, bezier, edgeLabel); totalLinks++; bool_edge= false; } /* * this method reads data for edges and nodes * called at a data element (usually nested inside a node an edge element) */ void Parser::readGraphMLElementData (QXmlStreamReader &xml){ QXmlStreamAttributes xmlStreamAttr = xml.attributes(); key_id = xmlStreamAttr.value("key").toString(); key_value=xml.text().toString(); qDebug()<< "Parser::readGraphMLElementData() - key_id: " << key_id << " key_value "<< key_value; if (key_value.trimmed() == "") { qDebug()<< "Parser::readGraphMLElementData() - text: " << key_value; xml.readNext(); key_value=xml.text().toString(); qDebug()<< "Parser::readGraphMLElementData() - text: " << key_value; if ( key_value.trimmed() != "" ) { //if there's simple text after the StartElement, qDebug()<< "Parser::readGraphMLElementData() - key_id " << key_id << " value is simple text " <"+edge_target, QString::number(edgeWeight)+"|"+edgeColor +"|"+QString::number(edgeDirType)); } } else if ( ( keyName.value(key_id) == "value" || keyName.value(key_id) == "weight" ) && keyFor.value(key_id) == "edge" ) { conv_OK=false; edgeWeight= key_value.toFloat( &conv_OK ); if (!conv_OK) edgeWeight = 1.0; if (missingNode){ edgesMissingNodesHash.insert(edge_source+"===>"+edge_target, QString::number(edgeWeight)+"|"+edgeColor +"|"+QString::number(edgeDirType)); } qDebug()<< "Parser::readGraphMLElementData() - Data found. Edge value: " << key_value << " Using "<< edgeWeight << " for this edge"; } else if ( keyName.value(key_id) == "size of arrow" && keyFor.value(key_id) == "edge" ) { conv_OK=false; float temp = key_value.toFloat( &conv_OK ); if (!conv_OK) arrowSize = 1; else arrowSize = temp; qDebug()<< "Parser::readGraphMLElementData() - Data found. Edge arrow size: " << key_value << " Using "<< arrowSize << " for this edge"; } else if (keyName.value(key_id) == "label" && keyFor.value(key_id) == "edge" ){ edgeLabel = key_value; if (missingNode){ edgesMissingNodesHash.insert(edge_source+"===>"+edge_target, QString::number(edgeWeight)+"|"+edgeColor +"|"+QString::number(edgeDirType)); } qDebug()<< "Parser::readGraphMLElementData() - Data found. Edge label: " << edgeLabel << " for this edge"; } } /** * Reads node graphics data and properties: label, color, shape, size, coordinates, etc. */ void Parser::readGraphMLElementNodeGraphics(QXmlStreamReader &xml) { qDebug()<< "Parser::readGraphMLElementNodeGraphics() - element name " << xml.name().toString(); float tempX =-1, tempY=-1, temp=-1; QString color; QXmlStreamAttributes xmlStreamAttr = xml.attributes(); if ( xml.name() == "Geometry" ) { if ( xmlStreamHasAttribute ( xmlStreamAttr, "x") ) { conv_OK=false; tempX = xml.attributes().value("x").toString().toFloat (&conv_OK) ; if (conv_OK) randX = tempX; } if ( xmlStreamHasAttribute ( xmlStreamAttr, "y") ) { conv_OK=false; tempY = xml.attributes().value("y").toString().toFloat (&conv_OK) ; if (conv_OK) randY = tempY; } qDebug()<< "Parser::readGraphMLElementNodeGraphics() - Node Coordinates: " << tempX << " " << tempY << " Using coordinates" << randX<< " "< 0 ) { bool ok; edgeWeight = initEdgeWeight; edgeColor = initEdgeColor; edgeDirType=EDGE_DIRECTED; qDebug()<<"Parser::createMissingNodeEdges() - edges to create " << count; QHash::const_iterator it = edgesMissingNodesHash.constBegin(); while (it != edgesMissingNodesHash.constEnd()) { qDebug()<<"Parser::createMissingNodeEdges() - creating missing edge " << it.key() << " data " << it.value() ; edgeMissingNodesList = (it.key()).split("===>"); if (! ((edgeMissingNodesList[0]).isEmpty() ) && !((edgeMissingNodesList[1]).isEmpty()) ) { source = nodeHash.value(edgeMissingNodesList[0], -666); target = nodeHash.value(edgeMissingNodesList[1], -666); if (source == -666 || target == -666 ) { //emit something that this node has not been declared continue; } edgeMissingNodesListData = (it.value()).split("|"); if (!edgeMissingNodesListData[0].isEmpty() ){ edgeWeight = edgeMissingNodesListData[0].toInt(&ok, 10); } if (!edgeMissingNodesListData[1].isEmpty() ){ edgeColor = edgeMissingNodesListData[1]; } if (!edgeMissingNodesListData[2].isEmpty() ){ if ( (edgeMissingNodesListData[2]).contains("2") ) edgeDirType=EDGE_RECIPROCAL_UNDIRECTED; } qDebug()<<"Parser::createMissingNodeEdges() - signal edgeCreate " << source << " -> " << target << " edgeDirType value " << edgeDirType; emit edgeCreate(source, target, edgeWeight, edgeColor, edgeDirType, arrows, bezier, edgeLabel); } ++it; } } else { qDebug()<<"Parser::createMissingNodeEdges() - nothing to do"; } } /** Tries to load a file as GML formatted network. If not it returns -1 */ bool Parser::loadGML(){ qDebug()<< "Parser::loadGML()"; QFile file ( fileName ); if ( ! file.open(QIODevice::ReadOnly )) { return false; } QTextStream ts( &file ); ts.setCodec(userSelectedCodecName.toUtf8()); QRegularExpression onlyDigitsExp("^\\d+$"); QStringList tempList; QString str; int fileLine=0; bool floatOK= false; bool isPlanar = false, graphKey=false, graphicsKey=false, edgeKey=false, nodeKey=false, graphicsCenterKey=false; Q_UNUSED(isPlanar); relationsList.clear(); node_id= QString::null; arrows=true; bezier=false; edgeDirType=EDGE_RECIPROCAL_UNDIRECTED; totalNodes=0; while (!ts.atEnd() ) { floatOK= false; fileContainsNodeCoords = false; nodeShape = initNodeShape; nodeColor = true; fileLine++; str= ts.readLine() ; str=str.simplified(); qDebug()<< "Parser::loadGML() - line" << fileLine <<":" << str; if ( isComment(str) ) continue; if ( fileLine == 1 && ( str.contains("vertices",Qt::CaseInsensitive) || str.contains("network",Qt::CaseInsensitive) || str.contains("digraph",Qt::CaseInsensitive) || str.contains("DL",Qt::CaseInsensitive) || str.contains("list",Qt::CaseInsensitive) || str.contains("graphml",Qt::CaseInsensitive) || str.contains("xml",Qt::CaseInsensitive) ) ) { qDebug()<< "*** Parser::loadGML(): Not a GML-formatted file. Aborting!!"; errorMessage = tr("Not an GML-formatted file. " "Non-comment line %1 includes prohibited strings (i.e GraphML)") .arg(fileLine); file.close(); return false; } if ( str.startsWith("comment",Qt::CaseInsensitive) ) { qDebug()<< "Parser::loadGML() - This is a comment. Continue."; continue; } if ( str.startsWith("creator",Qt::CaseInsensitive) ) { qDebug()<< "Parser::loadGML() - This is a creator description. Continue."; continue; } else if ( str.startsWith("graph",Qt::CaseInsensitive) ) { //describe a graph qDebug()<< "Parser::loadGML() - graph description list start"; graphKey = true; } else if ( str.startsWith("directed",Qt::CaseInsensitive) ) { //graph attribute declarations if (graphKey) { if ( str.contains("1")) { qDebug()<< "Parser::loadGML() - graph directed 1. A directed graph."; edgeDirType=EDGE_DIRECTED; } else { qDebug()<< "Parser::loadGML() - graph directed 0. An undirected graph."; } } } else if ( str.startsWith("isPlanar",Qt::CaseInsensitive) ) { //key declarations if (graphKey) { if ( str.contains("1")) { qDebug()<< "Parser::loadGML() - graph isPlanar 1. Planar graph."; isPlanar = true; } else { isPlanar = false; } } } else if ( str.startsWith("node",Qt::CaseInsensitive) ) { //node declarations qDebug()<< "Parser::loadGML() - node description list starts"; nodeKey = true; } else if ( str.startsWith("id",Qt::CaseInsensitive) ) { //describes identification number for an object if ( nodeKey ) { totalNodes++; node_id = str.split(" ",QString::SkipEmptyParts).last(); if (!node_id.contains(onlyDigitsExp)) { errorMessage = tr("Not a proper GML-formatted file. " "Node id tag at line %1 has non-arithmetic value.") .arg(fileLine); return false; } qDebug()<< "Parser::loadGML() - id description " << "This node" << totalNodes <<"id"<< node_id; } } else if ( str.startsWith("label ",Qt::CaseInsensitive) ) { //describes label if ( nodeKey ) { nodeLabel = str.split(" ",QString::SkipEmptyParts).last().remove("\""); qDebug()<< "Parser::loadGML() - node label definition" << "node" << totalNodes <<"id"<< node_id << "label" << nodeLabel; //FIXME REMOVE ANY " } else if ( edgeKey ) { edgeLabel = str.split(" ",QString::SkipEmptyParts).last(); qDebug()<< "Parser::loadGML() - edge label definition" << "edge" << totalLinks << "label" << edgeLabel; } } else if ( str.startsWith("edge ",Qt::CaseInsensitive) ) { //edge declarations qDebug()<< "Parser::loadGML() - edge description list start"; edgeKey = true; totalLinks++; } else if ( str.startsWith("source ",Qt::CaseInsensitive) ) { if (edgeKey) { edge_source = str.split(" ",QString::SkipEmptyParts).last(); //if edge_source if (!edge_source.contains(onlyDigitsExp)) { errorMessage = tr("Not a proper GML-formatted file. " "Edge source tag at line %1 has non-arithmetic value.") .arg(fileLine); return false; } source = edge_source.toInt(0); qDebug()<< "Parser::loadGML() - edge source definition" << "edge source" << edge_source; } } else if ( str.startsWith("target ",Qt::CaseInsensitive) ) { if (edgeKey) { edge_target = str.split(" ",QString::SkipEmptyParts).last(); if (!edge_source.contains(onlyDigitsExp)) { errorMessage = tr("Not a proper GML-formatted file. " "Edge target tag at line %1 has non-arithmetic value.") .arg(fileLine); return false; } target = edge_target.toInt(0); qDebug()<< "Parser::loadGML() - edge target definition" << "edge target" << edge_target; } } else if ( str.startsWith("graphics",Qt::CaseInsensitive) ) { //Describes graphics which are used to draw a particular object. if (nodeKey) { qDebug()<< "Parser::loadGML() - node graphics description list start"; } else if (edgeKey) { qDebug()<< "Parser::loadGML() - edge graphics description list start"; } graphicsKey = true; } else if ( str.startsWith("center",Qt::CaseInsensitive) ) { if (graphicsKey && nodeKey) { qDebug()<< "Parser::loadGML() - node graphics center start"; if ( str.contains("[", Qt::CaseInsensitive) ) { if ( str.contains("]", Qt::CaseInsensitive) && str.contains("x", Qt::CaseInsensitive) && str.contains("y", Qt::CaseInsensitive)) { str.remove("center"); str.remove("["); str.remove("]"); str = str.simplified(); tempList = str.split(" ",QString::SkipEmptyParts); randX = (tempList.at(1)).toFloat(&floatOK); if (!floatOK) { errorMessage = tr("Not a proper GML-formatted file. " "Node center tag at line %1 cannot be converted to float.") .arg(fileLine); return false; } randY = tempList.at(3).toFloat(&floatOK); if (!floatOK) { errorMessage = tr("Not a proper GML-formatted file. " "Node center tag at line %1 cannot be converted to float.") .arg(fileLine); return false; } qDebug()<< "Parser::loadGML() - node graphics center" << "x" << randX << "y" << randY; fileContainsNodeCoords = true; } else { graphicsCenterKey = true; } } } } else if ( str.startsWith("center",Qt::CaseInsensitive) && nodeKey && graphicsKey && graphicsCenterKey ) { //this is the case where the bracker [ is below the center tag } else if ( str.startsWith("type",Qt::CaseInsensitive) ) { if (graphicsKey && nodeKey) { qDebug()<< "Parser::loadGML() - node graphics type start"; nodeShape = str.split(" ",QString::SkipEmptyParts).last(); if (nodeShape.isNull() || nodeShape.isEmpty() ) { errorMessage = tr("Not a proper GML-formatted file. " "Node type tag at line %1 has no value.") .arg(fileLine); return false; } nodeShape.remove("\""); } } else if ( str.startsWith("fill",Qt::CaseInsensitive) ) { if (graphicsKey && nodeKey) { qDebug()<< "Parser::loadGML() - node graphics fill start"; nodeColor = str.split(" ",QString::SkipEmptyParts).last(); if (nodeColor.isNull() || nodeColor.isEmpty() ) { errorMessage = tr("Not a proper GML-formatted file. " "Node fill tag at line %1 has no value.") .arg(fileLine); return false; } } } else if ( str.startsWith("]",Qt::CaseInsensitive) ) { if (nodeKey && graphicsKey && graphicsCenterKey ) { qDebug()<< "Parser::loadGML() - node graphics center ends"; graphicsCenterKey = false; } else if (graphicsKey) { qDebug()<< "Parser::loadGML() - graphics list ends"; graphicsKey = false; } else if (nodeKey && !graphicsKey) { qDebug()<< "Parser::loadGML() - node description list ends"; nodeKey = false; if (!fileContainsNodeCoords) { randX=rand()%gwWidth; randY=rand()%gwHeight; } qDebug()<<" *** Creating node "<< node_id << " at "<< randX <<","<< randY <<" label "<" + edge_target; } emit edgeCreate(source,target, edgeWeight, edgeColor, edgeDirType, arrows, bezier, edgeLabel); } else if (graphKey) { qDebug()<< "Parser::loadGML() - graph description list ends"; graphKey = false; } } } if (relationsList.count() == 0 ) { emit addRelation( "unnamed" ); } //The network has been loaded. Tell MW the statistics and network type emit networkFileLoaded(FILE_GML, fileName, networkName, totalNodes, totalLinks, edgeDirType); qDebug() << "Parser-loadGML()"; return true; } /** Tries to load the file as Dot (Graphviz) formatted network. If not it returns -1 */ bool Parser::loadDot(){ qDebug("\n\nParser: loadDotNetwork"); int fileLine=0, aNum=-1; int start=0, end=0, next=0; QString label, node, nodeLabel, fontName, fontColor, edgeShape, edgeColor, edgeLabel, networkLabel; QString str, temp, prop, value ; QStringList lineElement; nodeColor="red"; edgeColor="black"; nodeShape=""; edgeWeight=1.0; float nodeValue=1.0; bool netProperties = false; QStringList labels; QList nodeSequence; //holds edges QList nodesDiscovered; //holds nodes; relationsList.clear(); edgeDirType=EDGE_DIRECTED; arrows=true; bezier=false; source=0, target=0; QFile file ( fileName ); if ( ! file.open(QIODevice::ReadOnly )) return false; QTextStream ts( &file ); ts.setCodec(userSelectedCodecName.toUtf8()); totalNodes=0; while (!ts.atEnd() ) { str= ts.readLine() ; str=str.simplified(); str=str.trimmed(); if ( isComment (str) ) continue; fileLine++; qDebug ()<<"Reading fileLine "<< fileLine; qDebug ()<< str; if ( fileLine == 1 ) { if ( str.contains("vertices",Qt::CaseInsensitive) //Pajek || str.contains("network",Qt::CaseInsensitive) //Pajek? || str.contains("[",Qt::CaseInsensitive) // GML || str.contains("DL",Qt::CaseInsensitive) //DL format || str.contains("list",Qt::CaseInsensitive) //list || str.startsWith("",Qt::CaseInsensitive) && str.contains ("=",Qt::CaseInsensitive) && !netProperties ) { qDebug()<< "* A node definition must be here: \n" << str; end=str.indexOf('['); if (end!=-1) { temp=str.right(str.size()-end-1); //keep the properties temp=temp.remove(']'); temp=temp.remove(';'); node=str.left(end-1); node=node.remove('\"'); qDebug()<<"node named "<",Qt::CaseInsensitive) && !str.contains ("=",Qt::CaseInsensitive) && !netProperties ) { qDebug()<< "* A node definition without properties must be here ..." << str; end=str.indexOf(';'); if (end!=-1) { node=str.left(str.size()-end); //keep the properties qDebug()<<"node named "<",Qt::CaseInsensitive) ){ //non directed = symmetric links if ( str.contains("--",Qt::CaseInsensitive) ) nodeSequence=str.split("--"); else nodeSequence=str.split("-"); edgeDirType=EDGE_RECIPROCAL_UNDIRECTED; } else { //is directed nodeSequence=str.split("->"); edgeDirType=EDGE_DIRECTED; } //Create all nodes defined in nodeSequence for ( QList::iterator it=nodeSequence.begin(); it!=nodeSequence.end(); it++ ) { node=(*it).simplified(); qDebug () << " nodeSequence node "<< node; if ( (aNum=nodesDiscovered.indexOf( node ) ) == -1) { //node not discovered before totalNodes++; randX=rand()%gwWidth; randY=rand()%gwHeight; qDebug()<<" *** Creating node "<< totalNodes << " at "<< randX <<","<< randY <<" label "< 2 ) {//there is a node definition here node=str.left(start).remove('\"').simplified(); qDebug()<<"node label: "< void print_queue(T& q) { qDebug() << "print_queue() "; while(!q.empty()) { qDebug() << q.top().key << " value: " << q.top().value << " "; q.pop(); } qDebug() << endl; } /** * @brief A method to load a weighted edge list formatted file. * @param delimiter * @return * This method can read and parse edgelist formated files * where edge source and target are either named with numbers or with labels * That is the following formats can be parsed: # edgelist with node numbers 1 2 1 1 3 2 1 6 2 1 8 2 ... # edgelist with node labels actor1 actor2 1 actor2 actor4 2 actor1 actor3 1 actorX actorY 3 name othername 1 othername somename 2 .... */ bool Parser::loadEdgeListWeighed(const QString &delimiter){ qDebug() << "Parser::loadEdgeListWeighed() - column delimiter" << delimiter ; QFile file ( fileName ); if ( ! file.open(QIODevice::ReadOnly )) return false; QTextStream ts( &file ); ts.setCodec(userSelectedCodecName.toUtf8()); QMap nodeMap; // use a minimum priority queue to order Actors by their value // so that we can create the discovered nodes by either their increasing nodeNumber // (if nodesWithLabels == true) or by their actual number in the file (if nodesWithLabels == false). priority_queue, CompareActors> nodeQ; QHash edgeList; QString str, edgeKey,edgeKeyDelimiter="====>" ; QStringList lineElement, edgeElement; // one or more digits QRegularExpression onlyDigitsExp("^\\d+$"); bool nodesWithLabels = false; bool floatOK = false; int fileLine = 1; totalNodes=0; edgeWeight=1.0; edgeDirType=EDGE_DIRECTED; arrows=true; bezier=false; relationsList.clear(); qDebug()<< "*** Parser::loadEdgeListWeighed() - Initial file parsing " "to test integrity and edge naming scheme"; while ( !ts.atEnd() ) { str= ts.readLine() ; qDebug()<< "Parser::loadEdgeListWeighed() - str" << str; str=str.simplified(); qDebug()<< "Parser::loadEdgeListWeighed() - simplified str" << str; if ( isComment(str) ) continue; if ( str.contains("vertices",Qt::CaseInsensitive) || str.contains("network",Qt::CaseInsensitive) || str.contains("graph",Qt::CaseInsensitive) || str.contains("digraph",Qt::CaseInsensitive) || str.contains("DL",Qt::CaseInsensitive) || str.contains("list",Qt::CaseInsensitive) || str.contains("graphml",Qt::CaseInsensitive) || str.contains("xml",Qt::CaseInsensitive) ) { qDebug()<< "Parser::loadEdgeListWeighed() - " "Not a Weighted list-formatted file. Aborting!!"; file.close(); errorMessage = tr("Not an EdgeList-formatted file. " "A non-comment line includes prohibited strings (i.e GraphML)"); return false; } lineElement=str.split(delimiter); if ( lineElement.count() != 3 ) { qDebug()<< "*** Parser::loadEdgeListWeighed() - " "Not a Weighted list-formatted file. Aborting!!"; file.close(); errorMessage = tr("Not a properly EdgeList-formatted file. " "Row %1 has not 3 elements as expected (i.e. source, target, weight)") .arg(fileLine); return false; } edge_source = lineElement[0]; edge_target = lineElement[1]; edge_weight = lineElement[2]; qDebug()<< "Parser::loadEdgeListWeighed() - Dissecting line - " "source:" << edge_source << "target:" << edge_target << "weight:" << edge_weight; if (!edge_source.contains(onlyDigitsExp)) { qDebug()<< "Parser::loadEdgeListWeighed() - node named by non-digit only string. " "nodesWithLabels = true"; nodesWithLabels = true; } if (!edge_target.contains(onlyDigitsExp)) { qDebug()<< "Parser::loadEdgeListWeighed() - node named by non-digit only string. " "nodesWithLabels = true"; nodesWithLabels = true; } fileLine ++; } ts.seek(0); qDebug()<< "*** Parser::loadEdgeListWeighed() - Initial file parsing finished. " "This is really a weighted edge list. Proceed to main parsing"; while ( !ts.atEnd() ) { str= ts.readLine() ; qDebug()<< "Parser::loadEdgeListWeighed() - str" << str; str=str.simplified(); qDebug()<< "Parser::loadEdgeListWeighed() - simplified str" << str; if ( isComment(str) ) continue; lineElement=str.split(delimiter); edge_source = lineElement[0]; edge_target = lineElement[1]; edge_weight = lineElement[2]; qDebug()<< "Parser::loadEdgeListWeighed() - Dissecting line - " "source:" << edge_source << "target:" << edge_target << "weight:" << edge_weight; if ( ! nodeMap.contains(edge_source) ) { totalNodes++; Actor sourceActor; sourceActor.key = edge_source; if (nodesWithLabels) { sourceActor.value = totalNodes; // order by an increasing totalNodes index nodeQ.push( sourceActor ); nodeMap.insert(edge_source, totalNodes); } else { sourceActor.value = edge_source.toInt(); // order by the actual actor number in the file nodeQ.push( sourceActor ); nodeMap.insert(edge_source, edge_source.toInt() ); } qDebug()<< "Parser::loadEdgeListWeighed() - source, new node named" << edge_source << "totalNodes" << totalNodes << "nodeMap.count" << nodeMap.count(); } else { qDebug()<< "Parser::loadEdgeListWeighed() - source already found, continue"; } if ( ! nodeMap.contains(edge_target) ) { totalNodes++; Actor targetActor; targetActor.key = edge_target; if (nodesWithLabels) { targetActor.value = totalNodes ; // order by an increasing totalNodes index nodeQ.push( targetActor ); nodeMap.insert(edge_target, totalNodes); } else { targetActor.value = edge_target.toInt(); // order by the actual actor number in the file nodeQ.push( targetActor ); nodeMap.insert(edge_target, edge_target.toInt() ); } qDebug()<< "Parser::loadEdgeListWeighed() - target, new node named" << edge_target << "totalNodes" << totalNodes << "nodeMap.count" << nodeMap.count(); } else { qDebug()<< "Parser::loadEdgeListWeighed() - target already found, continue"; } edgeWeight = edge_weight.toFloat(&floatOK); if (floatOK) { qDebug()<< "Parser::loadEdgeListWeighed() - read edge weight" << edgeWeight; } else { edgeWeight = 1.0; qDebug()<< "Parser::loadEdgeListWeighed() - error reading edge weight." "Using default edgeWeight" << edgeWeight; } edgeKey = edge_source + edgeKeyDelimiter + edge_target; if ( ! edgeList.contains( edgeKey ) ) { qDebug()<< "Parser::loadEdgeListWeighed() - inserting edgeKey" << edgeKey << "in edgeList with weight" << edgeWeight; edgeList.insert( edgeKey, edgeWeight ); totalLinks++; } } //end ts.stream while here file.close(); qDebug() << "Parser::loadEdgeListWeighed() - finished reading file, " "start creating nodes and edges"; //print_queue(nodeQ); // create nodes one by one while (!nodeQ.empty()) { Actor node = nodeQ.top(); nodeQ.pop(); randX=rand()%gwWidth; randY=rand()%gwHeight; if (nodesWithLabels) { qDebug() << "Parser::loadEdgeListWeighed() - creating node named" << node.key << "numbered" << node.value << "at position" << QPointF(randX, randY); emit createNode( node.value, initNodeSize, initNodeColor, initNodeNumberColor, initNodeNumberSize, node.key, initNodeLabelColor, initNodeLabelSize, QPointF(randX, randY), initNodeShape, false ); } else { qDebug() << "Parser::loadEdgeListWeighed() - creating node named" << node.key << "numbered" << node.key.toInt() << "at position" << QPointF(randX, randY); emit createNode( node.key.toInt(), initNodeSize, initNodeColor, initNodeNumberColor, initNodeNumberSize, node.key, initNodeLabelColor, initNodeLabelSize, QPointF(randX, randY), initNodeShape, false ); } } //create edges one by one QHash::const_iterator edge = edgeList.constBegin(); while (edge!= edgeList.constEnd()) { qDebug() << "Parser::loadEdgeListWeighed() - creating edge named" << edge.key() << " weight " << edge.value(); edgeElement=edge.key().split(edgeKeyDelimiter); if (nodesWithLabels) { source = nodeMap.value( edgeElement[0] ) ; target = nodeMap.value( edgeElement[1] ) ; } else { source = edgeElement[0].toInt() ; target = edgeElement[1].toInt() ; } edgeWeight = edge.value(); emit edgeCreate(source, target, edgeWeight, initEdgeColor, edgeDirType, arrows, bezier); ++edge; } if (relationsList.count() == 0) { emit addRelation("unnamed"); } //The network has been loaded. Tell MW the statistics and network type emit networkFileLoaded(FILE_EDGELIST_WEIGHTED, fileName, networkName, totalNodes, totalLinks, edgeDirType); qDebug() << "Parser::loadEdgeListWeighed() - END. Returning."; return true; } bool Parser::loadEdgeListSimple(const QString &delimiter){ qDebug() << "Parser::loadEdgeListSimple() - column delimiter" << delimiter ; QFile file ( fileName ); if ( ! file.open(QIODevice::ReadOnly )) return false; QTextStream ts( &file ); ts.setCodec(userSelectedCodecName.toUtf8()); // set the userselectedCodec QString str, edgeKey,edgeKeyDelimiter="====>" ; QStringList lineElement,edgeElement; int columnCount=0; int fileLine = 0; bool nodesWithLabels= false; //@TODO Always use nodesWithLabels= true QMap nodeMap; // use a minimum priority queue to order Actors by their value // so that we can create the discovered nodes by either their increasing nodeNumber // (if nodesWithLabels == true) or by their actual number in the file (if nodesWithLabels == false). priority_queue, CompareActors> nodeQ; QHash edgeList; QRegularExpression onlyDigitsExp("^\\d+$"); totalNodes = 0; initEdgeWeight=1.0; edgeDirType=EDGE_DIRECTED; arrows=true; bezier=false; relationsList.clear(); qDebug()<< "*** Parser::loadEdgeListSimple() - Initial file parsing " "to test integrity and edge naming scheme"; while ( !ts.atEnd() ) { fileLine++; str= ts.readLine() ; qDebug()<< "Parser::loadEdgeListSimple() - line " << fileLine << endl << str; str=str.simplified(); if ( isComment(str) ) continue; if ( str.contains("vertices",Qt::CaseInsensitive) || str.contains("network",Qt::CaseInsensitive) || str.contains("graph",Qt::CaseInsensitive) || str.contains("digraph",Qt::CaseInsensitive) || str.contains("DL",Qt::CaseInsensitive) || str.contains("list",Qt::CaseInsensitive) || str.contains("graphml",Qt::CaseInsensitive) || str.contains("xml",Qt::CaseInsensitive) ) { qDebug()<< "*** Parser:loadEdgeListSimple(): Not an EdgeList-formatted file. Aborting!!"; errorMessage = tr("Not an EdgeList-formatted file. " "Non-comment line %1 includes prohibited strings (i.e GraphML)") .arg(fileLine); file.close(); return false; } lineElement=str.split(delimiter); for (QStringList::Iterator it1 = lineElement.begin(); it1!=lineElement.end(); ++it1) { edge_source = (*it1); if (!edge_source.contains(onlyDigitsExp)) { qDebug()<< "Parser::loadEdgeListSimple() - node named by non-digit only string. " "nodesWithLabels = true"; nodesWithLabels = true; } if ( edge_source == "0" ) { nodesWithLabels = true; } } } ts.seek(0); fileLine = 0; qDebug () << "Parser::loadEdgeListSimple() - Reset and read lines. nodesWithLabels" << nodesWithLabels; while ( !ts.atEnd() ) { fileLine++; str= ts.readLine() ; str=str.simplified(); qDebug()<< "Parser::loadEdgeListSimple() - line" << fileLine << endl << str; if ( isComment(str) ) continue; lineElement=str.split(delimiter); columnCount = 0; for (QStringList::Iterator it1 = lineElement.begin(); it1!=lineElement.end(); ++it1) { columnCount ++; if (columnCount == 1) { // source node edge_source = (*it1); qDebug()<< "Parser::loadEdgeListSimple() - Dissecting line - " "source node:" << edge_source; if ( ! nodeMap.contains(edge_source) ) { totalNodes++; Actor sourceActor; sourceActor.key = edge_source; if (nodesWithLabels) { sourceActor.value = totalNodes; // order by an increasing totalNodes index nodeQ.push( sourceActor ); nodeMap.insert(edge_source, totalNodes); } else { sourceActor.value = edge_source.toInt(); // order by the actual actor number in the file nodeQ.push( sourceActor ); nodeMap.insert(edge_source, edge_source.toInt() ); } qDebug()<< "Parser::loadEdgeListSimple() - source, new node named" << edge_source << "totalNodes" << totalNodes << "nodeMap.count" << nodeMap.count(); } else { qDebug()<< "Parser::loadEdgeListWeighed() - source already found, continue"; } } else { // target nodes edge_target= (*it1); qDebug()<< "Parser::loadEdgeListSimple() - Dissecting line - " "target node:" << edge_target; if ( ! nodeMap.contains(edge_target) ) { totalNodes++; Actor targetActor; targetActor.key = edge_target; if (nodesWithLabels) { targetActor.value = totalNodes ; // order by an increasing totalNodes index nodeQ.push( targetActor ); nodeMap.insert(edge_target, totalNodes); } else { targetActor.value = edge_target.toInt(); // order by the actual actor number in the file nodeQ.push( targetActor ); nodeMap.insert(edge_target, edge_target.toInt() ); } qDebug()<< "Parser::loadEdgeListSimple() - target, new node named" << edge_target << "totalNodes" << totalNodes << "nodeMap.count" << nodeMap.count(); } else { qDebug()<< "Parser::loadEdgeListSimple() - target already found, continue"; } } if (columnCount > 1) { edgeKey = edge_source + edgeKeyDelimiter + edge_target; if ( ! edgeList.contains( edgeKey ) ) { qDebug()<< "Parser::loadEdgeListSimple() - inserting edgeKey" << edgeKey << "in edgeList with initial weight" << initEdgeWeight; edgeList.insert( edgeKey, initEdgeWeight ); totalLinks++; } else { // if edge already discovered, then increase its weight by 1 edgeWeight = edgeList.value(edgeKey); edgeWeight = edgeWeight + 1; qDebug()<< "Parser::loadEdgeListSimple() - edgeKey" << edgeKey << "found before, adding in edgeList with increased weight" << edgeWeight; edgeList.insert( edgeKey, edgeWeight ); } } } // end for QStringList::Iterator } //end ts.stream while here file.close(); // create nodes one by one while (!nodeQ.empty()) { Actor node = nodeQ.top(); nodeQ.pop(); randX=rand()%gwWidth; randY=rand()%gwHeight; if (nodesWithLabels) { qDebug() << "Parser::loadEdgeListSimple() - creating node named" << node.key << "numbered" << node.value << "at position" << QPointF(randX, randY); emit createNode( node.value, initNodeSize, initNodeColor, initNodeNumberColor, initNodeNumberSize, node.key, initNodeLabelColor, initNodeLabelSize, QPointF(randX, randY), initNodeShape, false ); } else { qDebug() << "Parser::loadEdgeListSimple() - creating node named" << node.key << "numbered" << node.key.toInt() << "at position" << QPointF(randX, randY); emit createNode( node.key.toInt(), initNodeSize, initNodeColor, initNodeNumberColor, initNodeNumberSize, node.key, initNodeLabelColor, initNodeLabelSize, QPointF(randX, randY), initNodeShape, false ); } } //create edges one by one QHash::const_iterator edge = edgeList.constBegin(); while (edge!= edgeList.constEnd()) { qDebug() << "Parser::loadEdgeListWeighed() - creating edge named" << edge.key() << " weight " << edge.value(); edgeElement=edge.key().split(edgeKeyDelimiter); if (nodesWithLabels) { source = nodeMap.value( edgeElement[0] ) ; target = nodeMap.value( edgeElement[1] ) ; } else { source = edgeElement[0].toInt() ; target = edgeElement[1].toInt() ; } edgeWeight = edge.value(); emit edgeCreate(source, target, edgeWeight, initEdgeColor, edgeDirType, arrows, bezier); ++edge; } if (relationsList.count() == 0) { emit addRelation("unnamed"); } //The network has been loaded. Tell MW the statistics and network type emit networkFileLoaded(FILE_EDGELIST_SIMPLE, fileName, networkName, totalNodes, totalLinks, edgeDirType); qDebug() << "Parser-loadEdgeListSimple() ending and returning..."; return true; } //Returns true if QString str is a comment inside the network file. bool Parser::isComment(QString str){ if ( str.startsWith("#", Qt::CaseInsensitive) || str.startsWith("/*", Qt::CaseInsensitive) || str.startsWith("%", Qt::CaseInsensitive) || str.startsWith("/*", Qt::CaseInsensitive) || str.startsWith("//", Qt::CaseInsensitive) || str.isEmpty() ) { qDebug () << "Parser::isComment() - Comment or an empty line was found. " "Skipping..."; return true; } return false; } socnetv-2.2/src/parser.h000755 000765 000024 00000016117 13040734202 016265 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt parser.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef PARSER_H #define PARSER_H #include #include #include #include #include #include #include class QXmlStreamReader; class QXmlStreamAttributes; /** * @brief The Actor struct * Used in loadEdgeListWeighed and loadEdgeListSimple */ struct Actor { QString key; int value; }; /** * @brief The CompareActors class * Implements a min-priority queue * Used in loadEdgeListWeighed */ class CompareActors { public: bool operator()(Actor& t1, Actor& t2) { if (t1.value== t2.value) return t1.key > t2.key ; // qDebug () << t1.value << " > " << t2.value << "?" // << ( t1.value > t2.value ) ; return t1.value > t2.value; //minimum priority // Returns true if t2.value smaller than t1.value } }; /** * @brief The Parser class * Main class for network file parsing and loading * Supports GraphML, Pajek, Adjacency, Graphviz, UCINET, EdgeLists etc */ class Parser : public QObject { Q_OBJECT public: Parser(const QString fn, const QString codec, const int iNS, const QString iNC, const QString iNSh, const QString iNNC, const int iNNS, const QString iNLC, const int iNLS , const QString iEC, const int w, const int h, const int format, const int sm_mode, const QString delim=QString::null); ~Parser(); void run(); bool loadPajek(); bool loadAdjacency(); bool loadDot(); bool loadGraphML(); bool loadGML(); bool loadGW(); bool loadDL(); bool loadEdgeListSimple(const QString &delimiter); bool loadEdgeListWeighed(const QString &delimiter); bool loadTwoModeSociomatrix(); void readDotProperties(QString str, float &, QString &label, QString &shape, QString &color, QString &fontName, QString &fontColor ); bool readGraphML(QXmlStreamReader &); void readGraphMLElementGraph(QXmlStreamReader &); void readGraphMLElementNode (QXmlStreamReader &); void endGraphMLElementNode (QXmlStreamReader &); void readGraphMLElementEdge (QXmlStreamAttributes &); void endGraphMLElementEdge (QXmlStreamReader &); void readGraphMLElementData (QXmlStreamReader &); void readGraphMLElementUnknown (QXmlStreamReader &); void readGraphMLElementKey (QXmlStreamAttributes &); bool xmlStreamHasAttribute( QXmlStreamAttributes &, QString ) const ; void readGraphMLElementDefaultValue(QXmlStreamReader &); void readGraphMLElementNodeGraphics (QXmlStreamReader &); void readGraphMLElementEdgeGraphics (QXmlStreamReader &); void createMissingNodeEdges(); bool isComment(QString str); void createRandomNodes(const int &fixedNum=1,const QString &label=QString::null, const int &newNodes=1); void loadFileError(const QString &errorMessage); signals: void addRelation( const QString & relName, const bool &changeRelation=false); void relationSet( int ); void createNode( const int &num, const int &size, const QString &color, const QString &numColor, const int &numSize, const QString &label, const QString &lColor, const int &lSize, const QPointF &p, const QString &shape, const bool &signalMW=false); void createNodeAtPosRandom(const bool &signalMW=false); void createNodeAtPosRandomWithLabel (const int &num, const QString &label, const bool &signalMW=false ); void edgeCreate (const int &source, const int &target, const float &weight, const QString &color, const int &edgeDirType, const bool &arrows, const bool &bezier, const QString &edgeLabel=QString::null, const bool &signalMW=false); void networkFileLoaded(int fileType, QString fileName, QString netName, int totalNodes, int totalLinks, bool edgeDirType, const QString &message=QString::null); void removeDummyNode (int); void finished(QString); protected: private: QHash nodeHash; QHash keyFor, keyName, keyType, keyDefaultValue ; QHash edgesMissingNodesHash; QStringList edgeMissingNodesList,edgeMissingNodesListData, relationsList; QMultiMap firstModeMultiMap, secondModeMultiMap; QXmlStreamReader *xml; QString fileName, userSelectedCodecName, networkName, initNodeColor; QString initEdgeColor, initNodeShape, initNodeNumberColor, initNodeLabelColor; QString initEdgeLabel, delimiter, errorMessage; QString nodeColor, edgeColor, edgeType, nodeShape, nodeLabel, edgeLabel; QString nodeNumberColor, nodeLabelColor; QString key_id, key_value, key_name, key_what, key_type; QString node_id, edge_id, edge_source, edge_target, edge_weight, edge_directed; int gwWidth, gwHeight; int totalLinks, totalNodes, fileFormat, two_sm_mode, edgeDirType; int initNodeSize, initNodeNumberSize, nodeNumberSize, initNodeLabelSize; int nodeLabelSize, source, target, nodeSize; float initEdgeWeight, edgeWeight, arrowSize; float bez_p1_x,bez_p1_y, bez_p2_x, bez_p2_y; bool missingNode; bool arrows, bezier, conv_OK; bool bool_key, bool_node, bool_edge, fileContainsNodeColors; bool fileContainsNodeCoords, fileContainsLinkColors; bool fileContainsLinkLabels; double randX, randY; }; #endif socnetv-2.2/src/src.qrc000755 000765 000024 00000011022 13040701535 016107 0ustar00dimitrisstaff000000 000000 images/properties.png images/selectall.png images/selectnone.png images/new.png images/open.png images/save.png images/saved.png images/exit.png images/add.png images/remove.png images/print.png images/help.png images/color.png images/colorize.png images/find.png images/letters.png images/line.png images/node.png images/nodes.png images/sm.png images/dm.png images/socnetv.png images/socnetv-32px.png images/net.png images/net1.png images/net2.png images/net3.png images/box.png images/circle.png images/diamond.png images/triangle.png images/ellipse.png images/symmetry.png images/plines.png images/zoomin.png images/zoomout.png images/connect.png images/disconnect.png images/resize.png images/nodecolor.png images/view.png images/diameter.png images/distance.png images/back.png images/forward.png images/home.png images/sw.png images/erdos.png images/circular.png images/clique.png images/walk.png images/triad.png images/avdistance.png images/symmetrize.png images/nodeout.png images/nodein.png images/gridlines.png images/webcrawler.png images/prestige.png images/centrality.png images/eccentricity.png images/prevrelation.png images/addrelation.png images/nextrelation.png images/texteditor.png images/networkfile.png images/random.png images/filter.png images/import.png images/force.png images/scalefree.png images/rotateleft.png images/rotateright.png images/appsettings.png images/spider.png images/image.png images/pdf.png images/reset.png images/socnetv-logo.png images/star.png images/help-hint.png images/bugs.png images/edgeweight.png images/qt.png images/download.png images/nodelabel.png images/nodelabelsize.png images/nodenumber.png images/nodenumbercolor.png images/nodeshape.png images/nodelabelcolor.png images/nodenumbersize.png images/edgecolor.png images/petersengraph.png images/cliquenew.png images/edit-rename.png images/subgraphstar.png images/subgraphline.png images/subgraphcycle.png images/clustering.png images/adjacencyplot.png images/similarity.png images/distances.png images/connectivity.png images/hierarchical.png images/clucof.png images/symmetry-edge.png images/laplacian.png images/degreematrix.png images/invertmatrix.png images/transposematrix.png images/cocitation.png socnetv-2.2/src/texteditor.cpp000755 000765 000024 00000026426 13040734201 017522 0ustar00dimitrisstaff000000 000000 /**************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt texteditor.cpp ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org *****************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include #include "texteditor.h" TextEditor::TextEditor(const QString &fileName, QWidget *parent, const bool &format) : QMainWindow(parent), formatHTML(format) { qDebug("TextEditor()"); textEdit = new QTextEdit; setCentralWidget(textEdit); createActions(); createMenus(); createToolBars(); createStatusBar(); readSettings(); connect(textEdit->document(), SIGNAL(contentsChanged()), this, SLOT(documentWasModified())); resize( 1024,768 ); setWindowState(Qt::WindowMaximized|Qt::WindowActive); if (!fileName.isEmpty()) loadFile(fileName); else setCurrentFile(""); } void TextEditor::closeEvent(QCloseEvent *event) { if (maybeSave()) { writeSettings(); event->accept(); } else { event->ignore(); } } void TextEditor::newFile() { if (maybeSave()) { textEdit->clear(); setCurrentFile(""); } } void TextEditor::open() { if (maybeSave()) { QString fileName = QFileDialog::getOpenFileName(this); if (!fileName.isEmpty()) loadFile(fileName); } } bool TextEditor::save() { if (curFile.isEmpty()) { return saveAs(); } else { return saveFile(curFile); } } bool TextEditor::saveAs() { QString fileName = QFileDialog::getSaveFileName(this, tr("Save file"), curFile); if (fileName.isEmpty()) return false; return saveFile(fileName); } void TextEditor::documentWasModified() { setWindowModified(textEdit->document()->isModified()); } void TextEditor::createActions() { newAct = new QAction(QIcon(":/images/new.png"), tr("&New"), this); newAct->setShortcut(tr("Ctrl+N")); newAct->setStatusTip(tr("Create a new file")); connect(newAct, SIGNAL(triggered()), this, SLOT(newFile())); openAct = new QAction(QIcon(":/images/open.png"), tr("&Open..."), this); openAct->setShortcut(tr("Ctrl+O")); openAct->setStatusTip(tr("Open an existing file")); connect(openAct, SIGNAL(triggered()), this, SLOT(open())); saveAct = new QAction(QIcon(":/images/save.png"), tr("&Save"), this); saveAct->setShortcut(tr("Ctrl+S")); saveAct->setStatusTip(tr("Save the document to disk")); connect(saveAct, SIGNAL(triggered()), this, SLOT(save())); saveAsAct = new QAction(tr("Save &As..."), this); saveAsAct->setStatusTip(tr("Save the document under a new name")); connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs())); exitAct = new QAction(tr("E&xit"), this); exitAct->setShortcut(tr("Ctrl+Q")); exitAct->setStatusTip(tr("Exit the application")); connect(exitAct, SIGNAL(triggered()), this, SLOT(close())); cutAct = new QAction(QIcon(":/images/cut.png"), tr("Cu&t"), this); cutAct->setShortcut(tr("Ctrl+X")); cutAct->setStatusTip(tr("Cut the current selection's contents to the " "clipboard")); connect(cutAct, SIGNAL(triggered()), textEdit, SLOT(cut())); copyAct = new QAction(QIcon(":/images/copy.png"), tr("&Copy"), this); copyAct->setShortcut(tr("Ctrl+C")); copyAct->setStatusTip(tr("Copy the current selection's contents to the " "clipboard")); connect(copyAct, SIGNAL(triggered()), textEdit, SLOT(copy())); pasteAct = new QAction(QIcon(":/images/paste.png"), tr("&Paste"), this); pasteAct->setShortcut(tr("Ctrl+V")); pasteAct->setStatusTip(tr("Paste the clipboard's contents into the current " "selection")); connect(pasteAct, SIGNAL(triggered()), textEdit, SLOT(paste())); aboutAct = new QAction(tr("&About"), this); aboutAct->setStatusTip(tr("Show the application's About box")); connect(aboutAct, SIGNAL(triggered()), this, SLOT(about())); aboutQtAct = new QAction(tr("About &Qt"), this); aboutQtAct->setStatusTip(tr("Show the Qt library's About box")); connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt())); cutAct->setEnabled(false); copyAct->setEnabled(false); connect(textEdit, SIGNAL(copyAvailable(bool)), cutAct, SLOT(setEnabled(bool))); connect(textEdit, SIGNAL(copyAvailable(bool)), copyAct, SLOT(setEnabled(bool))); } void TextEditor::createMenus() { fileMenu = menuBar()->addMenu(tr("&File")); fileMenu->addAction(newAct); fileMenu->addAction(openAct); fileMenu->addAction(saveAct); fileMenu->addAction(saveAsAct); fileMenu->addSeparator(); fileMenu->addAction(exitAct); editMenu = menuBar()->addMenu(tr("&Edit")); editMenu->addAction(cutAct); editMenu->addAction(copyAct); editMenu->addAction(pasteAct); menuBar()->addSeparator(); helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu->addAction(aboutAct); helpMenu->addAction(aboutQtAct); } void TextEditor::createToolBars() { fileToolBar = addToolBar(tr("File")); fileToolBar->addAction(newAct); fileToolBar->addAction(openAct); fileToolBar->addAction(saveAct); editToolBar = addToolBar(tr("Edit")); editToolBar->addAction(cutAct); editToolBar->addAction(copyAct); editToolBar->addAction(pasteAct); } void TextEditor::createStatusBar() { statusBar()->showMessage(tr("Ready")); } void TextEditor::readSettings() { QSettings settings("SocNetV", "TextEditor"); QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint(); QSize size = settings.value("size", QSize(400, 400)).toSize(); resize(size); move(pos); } void TextEditor::writeSettings() { QSettings settings("SocNetV ", "TextEditor"); settings.setValue("pos", pos()); settings.setValue("size", size()); } bool TextEditor::maybeSave() { if (textEdit->document()->isModified()) { QMessageBox::StandardButton ret; ret = QMessageBox::warning(this, tr("TextEditor"), tr("The document has been modified.\n" "Do you want to save your changes?"), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); if (ret == QMessageBox::Save) return save(); else if (ret == QMessageBox::Cancel) return false; } return true; } void TextEditor::loadFile(const QString &fileName) { QFile file(fileName); if (!file.open(QFile::ReadOnly | QFile::Text)) { QMessageBox::warning(this, tr("SocNetV Editor"), tr("Cannot read file %1:\n%2.") .arg(fileName) .arg(file.errorString())); return; } QTextStream in(&file); QApplication::setOverrideCursor(Qt::WaitCursor); if (formatHTML) textEdit->setHtml(in.readAll()); else textEdit->setPlainText(in.readAll()); QApplication::restoreOverrideCursor(); setCurrentFile(fileName); statusBar()->showMessage(tr("File loaded"), 2000); file.close(); } bool TextEditor::saveFile(const QString &fileName) { QFile file(fileName); if (!file.open(QFile::WriteOnly | QFile::Text)) { QMessageBox::warning(this, tr("SocNetV Editor"), tr("Cannot write file %1:\n%2.") .arg(fileName) .arg(file.errorString())); return false; } QTextStream outText(&file); outText.setCodec("UTF-8"); QApplication::setOverrideCursor(Qt::WaitCursor); if (formatHTML) outText << textEdit->toHtml(); else outText << textEdit->toPlainText(); QApplication::restoreOverrideCursor(); setCurrentFile(fileName); statusBar()->showMessage(tr("File saved"), 2000); file.close(); return true; } void TextEditor::setCurrentFile(const QString &fileName) { curFile = fileName; textEdit->document()->setModified(false); setWindowModified(false); QString shownName; if (curFile.isEmpty()) shownName = "untitled.txt"; else shownName = strippedName(curFile); setWindowTitle(tr("%1[*] - %2").arg(shownName).arg(tr("SocNetV Editor"))); } QString TextEditor::strippedName(const QString &fullFileName) { return QFileInfo(fullFileName).fileName(); } //bool TextEditor::canInsertFromMimeData( const QMimeData *source ) const // { // if (source->hasImage()) // return true; // else // return QTextEdit::canInsertFromMimeData(source); // } //void TextEditor::insertFromMimeData( const QMimeData *source ) //{ // if (source->hasImage()) // { // QImage image = qvariant_cast(source->imageData()); // QTextCursor cursor = this->textCursor(); // QTextDocument *document = this->document(); // document->addResource(QTextDocument::ImageResource, QUrl("image"), image); // cursor.insertImage("image"); // } //} void TextEditor::about() { QMessageBox::about( this, "SocNetV Editor", " Part of Social Network Visualizer" "

Developer:
Dimitris V. Kalamaras
" "
email: dimitris.kalamaras@gmail.com" "

Note: This text editor was adapted from Trolltech's application example." "

This program is free software; you can redistribute it and/or modify" "
it under the terms of the GNU General Public License as published by" "
the Free Software Foundation; either version 3 of the License, or" "
(at your option) any later version.

" "

This program is distributed in the hope that it will be useful," "
but WITHOUT ANY WARRANTY; without even the implied warranty of" "
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the" "
GNU General Public License for more details.

" "

You should have received a copy of the GNU General Public License" "
along with this program; if not, write to the Free Software" "
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

"); } socnetv-2.2/src/texteditor.h000755 000765 000024 00000005460 13040734202 017163 0ustar00dimitrisstaff000000 000000 /**************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt texteditor.h ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org *****************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef TEXTEDITOR_H #define TEXTEDITOR_H #include //#include class QAction; class QMenu; class QTextEdit; class TextEditor : public QMainWindow { Q_OBJECT public: TextEditor(const QString &fileName , QWidget *parent=0 , const bool &format=false); protected: void closeEvent(QCloseEvent *event); private slots: void newFile(); void open(); bool save(); bool saveAs(); void about(); void documentWasModified(); //protected: // bool canInsertFromMimeData(const QMimeData *source) const; // void insertFromMimeData(const QMimeData *source) ; private: void createActions(); void createMenus(); void createToolBars(); void createStatusBar(); void readSettings(); void writeSettings(); bool maybeSave(); void loadFile(const QString &fileName); bool saveFile(const QString &fileName); void setCurrentFile(const QString &fileName); QString strippedName(const QString &fullFileName); QTextEdit *textEdit; QString curFile; bool formatHTML; QMenu *fileMenu; QMenu *editMenu; QMenu *helpMenu; QToolBar *fileToolBar; QToolBar *editToolBar; QAction *newAct; QAction *openAct; QAction *saveAct; QAction *saveAsAct; QAction *exitAct; QAction *cutAct; QAction *copyAct; QAction *pasteAct; QAction *aboutAct; QAction *aboutQtAct; }; #endif socnetv-2.2/src/vertex.cpp000755 000765 000024 00000071550 13040734201 016642 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt vertex.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include "vertex.h" #include //used for qDebug messages #include "graph.h" Vertex::Vertex(Graph* parent, const long &name, const int &val, const int &relation, const int &size, const QString &color, const QString &numColor, const int &numSize, const QString &label, const QString &labelColor, const int &labelSize, const QPointF &p, const QString &shape): parentGraph (parent) { qDebug() << "Vertex::Vertex() - "<< name << " setting values"; m_name=name; m_value=val; m_size=size; m_color=color; m_numberColor=numColor; m_numberSize=numSize; m_label=label; m_labelColor=labelColor; m_labelSize=labelSize; m_shape=shape; m_x=p.x(); m_y=p.y(); //FIXME outLinkColors list need update when we remove vertices/edges //outLinkColors.reserve(2000); m_outEdgeLabels.reserve(100); m_outEdges.reserve(100); m_inEdges.reserve(100); m_neighborhoodList.reserve(100); m_reciprocalEdges = new QHash; m_outEdgesCounter=0; m_inEdgesCounter=0; m_outDegree=0; m_inDegree=0; m_localDegree=0; m_Eccentricity=0; m_DC=0; m_SDC=0; m_DP=0; m_SDP=0; m_CC=0; m_SCC=0; m_BC=0; m_SBC=0; m_SC=0; m_SSC=0; m_IRCC=0; m_SIRCC=0; m_CLC=0; m_hasCLC=false; m_curRelation=relation; m_enabled = true; connect (this, SIGNAL (setEdgeVisibility ( int, int, int, bool) ), parent, SLOT (edgeVisibilitySet (int, int, int, bool)) ); } // constructor with default values Vertex::Vertex(const long int &name) { qDebug() << "Vertex::Vertex() - "<< name << " using default values"; m_name=name; m_value=1; m_size=9; m_color="black"; m_label=""; m_labelColor="black"; m_shape="circle"; m_outEdgesCounter=0; m_inEdgesCounter=0; m_Eccentricity=0; m_DC=0; m_SDC=0; m_DP=0; m_SDP=0; m_CC=0; m_SCC=0; m_BC=0; m_SBC=0; m_IRCC=0; m_SIRCC=0; m_SC=0; m_SSC=0; m_curRelation=0; } /** * @brief Vertex::relationSet * @param relation */ void Vertex::relationSet(int relation) { qDebug() << "Vertex::relationSet() - Current: " << m_curRelation << " new: " << relation; // first make false all edges of current relation edgeFilterByRelation(m_curRelation, false); // then make true all edges of new relation edgeFilterByRelation(relation, true); // update current relation m_curRelation=relation; } QString Vertex::colorToPajek(){ if (m_color.startsWith("#")) { return ("RGB"+m_color.right( m_color.size()-1 )).toUpper() ; } return m_color; } /** * @brief Vertex::edgeAddTo * Adds an outLink to target with weight w * @param target * @param weight */ void Vertex::edgeAddTo (const long &v2, const float &weight) { qDebug() <<"Vertex::edgeAddTo() - new outbound edge" << name() << " -> "<< v2 << " weight "<< weight << " relation " << m_curRelation; // do not use [] operator - silently creates an item if key do not exist m_outEdges.insertMulti( v2, rel_w_bool(m_curRelation, pair_f_b(weight, true) ) ); } /** * @brief Vertex::setOutEdgeEnabled * @param target * @param status */ void Vertex::setOutEdgeEnabled (long int target, bool status){ qDebug () << "Vertex::setOutEdgeEnabled - set outEdge to " << target << " as " << status << ". Finding outLink..."; QMutableHashIterator < int, rel_w_bool > it1 (m_outEdges); int linkTarget=0; float weight =0; int relation = 0; while ( it1.hasNext()) { it1.next(); relation = it1.value().first; if ( relation == m_curRelation ) { linkTarget=it1.key(); if ( linkTarget == target ) { weight = it1.value().second.first; qDebug() << " *** vertex " << m_name << " connected to " << linkTarget << " relation " << relation << " weight " << weight << " status " << it1.value().second.second; it1.setValue(rel_w_bool(m_curRelation, pair_f_b(weight, status) )); emit setEdgeVisibility (m_curRelation, m_name, target, status ); } } else { } } } /** * @brief Vertex::edgeAddFrom * @param source * @param weight */ void Vertex::edgeAddFrom (const long int &v1, const float &weight) { qDebug() <<"Vertex::edgeAddFrom() - new inbound edge" << name() << " <- "<< v1 << " weight "<< weight << " relation " << m_curRelation; m_inEdges.insertMulti( v1, rel_w_bool (m_curRelation, pair_f_b(weight, true) ) ); } void Vertex::changeOutEdgeWeight(long int target, float weight){ qDebug() << "Vertex::changeEdgeWeightTo " << target << " weight " << weight ; qDebug() << " *** m_outEdges.count " << m_outEdges.count(); qDebug() << "first find and remove old relation-weight pair" ; H_edges::iterator it1=m_outEdges.find(target); while (it1 != m_outEdges.end() ) { if ( it1.key() == target && it1.value().first == m_curRelation ) { it1=m_outEdges.erase(it1); } else { ++it1; } } qDebug() << " *** m_outEdges.count " << m_outEdges.count(); qDebug() << " create new relation-weight pair "; m_outEdges.insertMulti( target, rel_w_bool(m_curRelation, pair_f_b(weight, true) ) ); qDebug() << " *** m_outEdges.count " << m_outEdges.count(); } /** * @brief Vertex::edgeRemoveTo * finds and removes a link to vertex v2 * @param v2 */ void Vertex::edgeRemoveTo (long int v2) { qDebug() << "Vertex: edgeRemoveTo() - vertex " << m_name << " has " <0) { qDebug() << "Vertex::edgeRemoveTo() - checking all_outEdges"; H_edges::iterator it1=m_outEdges.find(v2); while (it1 != m_outEdges.end() && it1.key() == v2 ) { if ( it1.value().first == m_curRelation ) { qDebug() << " *** vertex " << m_name << " connected to " << it1.key() << " relation " << it1.value().first << " weight " << it1.value().second.first << " enabled ? " << it1.value().second.second << " Erasing outEdge from m_outEdges "; it1=m_outEdges.erase(it1); } else { ++it1; } } qDebug() << "Vertex::edgeRemoveTo() - vertex " << m_name << " now has " << outEdges() << " out-edges"; } else { qDebug() << "Vertex::edgeRemoveTo() - vertex " << m_name << " has no edges" ; } } /** * @brief Vertex::edgeRemoveFrom * @param v2 */ void Vertex::edgeRemoveFrom(long int v2){ qDebug() << "Vertex::edgeRemoveFrom() - vertex " << m_name << " has " << inEdges() << " in-edges. RemovingEdgeFrom " << v2 ; if (inEdges()>0) { qDebug() << "Vertex::edgeRemoveFrom() - checking all_inEdges"; H_edges::iterator it=m_inEdges.find(v2); while (it != m_inEdges.end() ) { if ( it.key() == v2 && it.value().first == m_curRelation ) { qDebug() << " *** vertex " << m_name << " connected from " << it.key() << " relation " << it.value().first << " weight " << it.value().second.first << " enabled ? " << it.value().second.second << " Erasing inEdge from m_inEdges "; it=m_inEdges.erase(it); } else { ++it; } } qDebug() << "Vertex::edgeRemoveFrom() - vertex " << m_name << " now has " << inEdges() << " in-links" ; } else { qDebug() << "Vertex::edgeRemoveFrom() - vertex " << m_name << " has no edges"; } } /** * @brief Vertex::edgeFilterByWeight Called from Graph parent to filter edges over or under a specified weight (m_threshold) * @param m_threshold * @param overThreshold */ void Vertex::edgeFilterByWeight(float m_threshold, bool overThreshold){ qDebug() << "Vertex::edgeFilterByWeight of vertex " << this->m_name; int target=0; float weight=0; QMutableHashIterator < int, rel_w_bool > it (m_outEdges); while ( it.hasNext()) { it.next(); if ( it.value().first == m_curRelation ) { target=it.key(); weight = it.value().second.first; if (overThreshold) { if ( weight >= m_threshold ) { qDebug() << "Vertex::edgeFilterByWeight() - edge to " << target << " has weight " << weight << ". It will be disabled. Emitting signal to Graph...."; it.setValue(rel_w_bool(m_curRelation, pair_f_b(weight, false) )); emit setEdgeVisibility (m_curRelation, m_name, target, false ); } else { qDebug() << "Vertex::edgeFilterByWeight() - edge to " << target << " has weight " << weight << ". It will be enabled. Emitting signal to Graph...."; it.setValue(rel_w_bool(m_curRelation, pair_f_b(weight, true) )); emit setEdgeVisibility (m_curRelation, m_name, target, true ); } } else { if ( weight <= m_threshold ) { qDebug() << "Vertex::edgeFilterByWeight() - edge to " << target << " has weight " << weight << ". It will be disabled. Emitting signal to Graph...."; it.setValue(rel_w_bool(m_curRelation, pair_f_b(weight, false) )); emit setEdgeVisibility (m_curRelation, m_name, target, false ); } else { qDebug() << "Vertex::edgeFilterByWeight() - edge to " << target << " has weight " << weight << ". It will be enabled. Emitting signal to Graph...."; it.setValue(rel_w_bool(m_curRelation, pair_f_b(weight, true) )); emit setEdgeVisibility (m_curRelation, m_name, target, true ); } } } } } /** * @brief Vertex::edgeFilterUnilateral Called from Graph to filter non-reciprocal edges If allRelations is true, then all relations are checked * @param toggle */ void Vertex::edgeFilterUnilateral(const bool &toggle){ qDebug() << "Vertex::edgeFilterUnilateral() of vertex " << this->m_name; int target=0; float weight=0; QMutableHashIterator < int, rel_w_bool > it (m_outEdges); while ( it.hasNext()) { it.next(); if ( it.value().first == m_curRelation ) { target=it.key(); weight = it.value().second.first; if (hasEdgeFrom(target)==0) { // \todo != weight would be more precise? if ( !toggle ) { qDebug() << "Vertex::edgeFilterUnilateral() - unilateral edge to " << target << " has weight " << weight << ". It will be disabled. Emitting signal to Graph...."; it.setValue(rel_w_bool(m_curRelation, pair_f_b(weight, false) )); emit setEdgeVisibility (m_curRelation, m_name, target, false ); } else { qDebug() << "Vertex::edgeFilterUnilateral() - unilateral edge to " << target << " has weight " << weight << ". It will be enabled. Emitting signal to Graph...."; it.setValue(rel_w_bool(m_curRelation, pair_f_b(weight, true) )); emit setEdgeVisibility (m_curRelation, m_name, target, true ); } } } } } /** * @brief Vertex::edgeFilterByRelation * Called from Graph to filter out all edges of a given relation * @param relation */ void Vertex::edgeFilterByRelation(int relation, bool status ){ qDebug() << "Vertex::edgeFilterByRelation() - Vertex " << this->m_name << " filtering edges of relation " << relation << " to " << status; int target=0; float weight =0; int edgeRelation=0; QMutableHashIterator < int, rel_w_bool > it1 (m_outEdges); while ( it1.hasNext()) { it1.next(); edgeRelation = it1.value().first; if ( edgeRelation == relation ) { target=it1.key(); weight = it1.value().second.first; qDebug() << "Vertex::edgeFilterByRelation() - outLink " << m_name << " -> " << target << " - emitting to GW to be " << status ; it1.setValue(rel_w_bool(relation, pair_f_b(weight, status) )); emit setEdgeVisibility ( relation, m_name, target, status ); } else { } } } /** * @brief Vertex::outEdges * Returns the number of active outbound arcs, aka the number of * outEdges, from this vertex for the current relation * @return long int */ long int Vertex::outEdges() { m_outEdgesCounter = 0; int relation=0; bool edgeStatus = false; H_edges::const_iterator it1=m_outEdges.constBegin(); while (it1 != m_outEdges.constEnd() ) { relation = it1.value().first; if ( relation == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_outEdgesCounter++; } } ++it1; } return m_outEdgesCounter; } /** * @brief Vertex::outEdgesConst * Returns the number of active outbound arcs * Needs to have outEdges called before the call to this method * @return long int */ long int Vertex::outEdgesConst() const { return m_outEdgesCounter; } /** * @brief Vertex::outEdgesEnabledHash * Returns a qhash of all enabled outEdges in the active relation * @return QHash* */ QHash* Vertex::outEdgesEnabledHash(const bool &allRelations){ //qDebug() << " Vertex::outEdgesEnabledHash() vertex " << this->name(); QHash *enabledOutEdges = new QHash; float m_weight=0; int relation = 0; bool edgeStatus=false; H_edges::const_iterator it1=m_outEdges.constBegin(); while (it1 != m_outEdges.constEnd() ) { relation = it1.value().first; if (!allRelations) { if ( relation == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_weight=it1.value().second.first; enabledOutEdges->insert(it1.key(), m_weight); // qDebug() << " Vertex::outEdgesEnabledHash() count:" // << enabledOutEdges->count(); } } } else { if ( !enabledOutEdges->contains(it1.key() )) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_weight=it1.value().second.first; enabledOutEdges->insert(it1.key(), m_weight); // qDebug() << " Vertex::outEdgesEnabledHash() count:" // << enabledOutEdges->count(); } } } ++it1; } // qDebug() << " Vertex::outEdgesEnabledHash() vertex " << this->name() // << " outEdges count:" // << enabledOutEdges->count(); return enabledOutEdges; } QHash* Vertex::outEdgesAllRelationsUniqueHash() { qDebug() << "Vertex::outEdgesAllRelationsUniqueHash() - v " << this->name(); QHash *outEdgesAll = new QHash; float m_weight=0; H_edges::const_iterator it1=m_outEdges.constBegin(); while (it1 != m_outEdges.constEnd() ) { if ( !outEdgesAll->contains(it1.key() )) { m_weight=it1.value().second.first; outEdgesAll->insert(it1.key(), m_weight); qDebug() << "Vertex::outEdgesAllRelationsUniqueHash() -" << this->name() << "->" << it1.key() << "relation"<< it1.value().first; } ++it1; } qDebug() << "Vertex::outEdgesAllRelationsUniqueHash() - v " << this->name() << " outEdges count:" << outEdgesAll->count(); return outEdgesAll; } /** * @brief Vertex::allReciprocalEdges * Returns a qhash of all reciprocal edges to neighbors in the active relation * @return QHash* */ QHash* Vertex::reciprocalEdgesHash(){ m_reciprocalEdges->clear(); float m_weight=0; int relation = 0; bool edgeStatus=false; H_edges::const_iterator it1=m_outEdges.constBegin(); // qDebug() << "Vertex::reciprocalEdgesHash() - of vertex " // << this->name() // << " - outEdges " << m_outEdges.count() // << " - Checking all edges for reciprocality"; while (it1 != m_outEdges.constEnd() ) { relation = it1.value().first; if ( relation == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_weight=it1.value().second.first; if (this->hasEdgeFrom (it1.key()) == m_weight ) { // qDebug() << "Vertex::reciprocalEdgesHash() - of vertex " // << this->name() // << "Found reciprocal edge with " << it1.key(); m_reciprocalEdges->insertMulti(it1.key(), m_weight); } } } ++it1; } qDebug() << "Vertex::reciprocalEdgesHash() - vertex" << this->name() << "reciprocalEdges:" << m_reciprocalEdges->count(); return m_reciprocalEdges; } /** * @brief Vertex::neighborhoodList * Returns a list of all neighbors mutually connected to this vertex in the active relation * Same as calling Vertex::reciprocalEdgesHash().keys() which returns a QList of int keys, * where each key is a vertex reciprocally connected to this one. * @return QList */ QList Vertex::neighborhoodList(){ m_neighborhoodList.clear(); float m_weight=0; int relation = 0; bool edgeStatus=false; H_edges::const_iterator it1=m_outEdges.constBegin(); while (it1 != m_outEdges.constEnd() ) { relation = it1.value().first; if ( relation == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_weight=it1.value().second.first; if (this->hasEdgeFrom (it1.key()) == m_weight ) { m_neighborhoodList << it1.key(); // qDebug() << "Vertex::neighborhoodList() - mutually connected neighbor=" // << it1.key() // << " m_neighborhoodList.count()" // << m_neighborhoodList.count(); } } } ++it1; } qDebug() << "Vertex::neighborhoodList() - of vertex " << this->name() << "final list" <localDegree(); return m_neighborhoodList; } /** * @brief Vertex::inEdges * Returns the number of active inbound arcs, aka the number of * inEdges, to this vertex for the current relation * @return long int */ long int Vertex::inEdges() { m_inEdgesCounter = 0; int relation=0; bool edgeStatus = false; H_edges::const_iterator it1=m_inEdges.constBegin(); while (it1 != m_inEdges.constEnd() ) { relation = it1.value().first; if ( relation == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_inEdgesCounter++; } } ++it1; } return m_inEdgesCounter; } /** * @brief Vertex::inEdgesEnabledHash * Returns a qhash of all enabled inEdges in the active relation * @return QHash* */ QHash* Vertex::inEdgesEnabledHash() { qDebug() << "Vertex::inEdgesEnabledHash()"; QHash *enabledInEdges = new QHash; float m_weight=0; int relation = 0; bool edgeStatus=false; H_edges::const_iterator it1=m_inEdges.constBegin(); while (it1 != m_inEdges.constEnd() ) { relation = it1.value().first; if ( relation == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_weight=it1.value().second.first; enabledInEdges->insert(it1.key(), m_weight); } } ++it1; } return enabledInEdges; } /** * @brief Vertex::inEdgesConst * Returns the number of active inbound arcs * Needs to have inEdges called before the call to this method * @return long int */ long int Vertex::inEdgesConst() const { return m_inEdgesCounter; } /** * @brief Vertex::degreeOut * Returns the degreeOut (the sum of all enabled outEdges weights) of this vertex * @return long int */ long int Vertex::degreeOut() { qDebug() << "Vertex::degreeOut()"; m_outDegree=0; float m_weight=0; int relation = 0; bool edgeStatus=false; H_edges::const_iterator it1=m_outEdges.constBegin(); while (it1 != m_outEdges.constEnd() ) { relation = it1.value().first; if ( relation == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_weight=it1.value().second.first; m_outDegree += m_weight; } } ++it1; } return m_outDegree; } long int Vertex::outDegreeConst() { return m_outDegree; } /** * @brief Vertex::degreeIn * Returns the degreeIn (the sum of all enabled inEdges weights) of this vertex * @return long int */ long int Vertex::degreeIn() { qDebug() << "Vertex::degreeIn()"; m_inDegree=0; float m_weight=0; int relation = 0; bool edgeStatus=false; H_edges::const_iterator it1=m_inEdges.constBegin(); while (it1 != m_inEdges.constEnd() ) { relation = it1.value().first; if ( relation == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_weight=it1.value().second.first; m_inDegree += m_weight; } } ++it1; } return m_inDegree; } long int Vertex::inDegreeConst() { return m_inDegree; } /** localDegree is the degreeOut + degreeIn minus the edges counted twice. */ long int Vertex::localDegree(){ long int v2=0; int relation = 0; bool edgeStatus=false; m_localDegree = (degreeOut() + degreeIn() ); H_edges::const_iterator it1=m_outEdges.constBegin(); while (it1 != m_outEdges.constEnd() ) { relation = it1.value().first; if ( relation == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { v2=it1.key(); if (this->hasEdgeFrom (v2) ) m_localDegree--; } } ++it1; } qDebug() << "Vertex:: localDegree() for " << this->name() << "is " << m_localDegree; return m_localDegree; } /** * @brief Vertex::hasEdgeTo * Checks if this vertex is outlinked to v2 and returns the weight of the edge * only if the outbound edge is enabled. * @param v2 * @return */ float Vertex::hasEdgeTo(const long int &v2, const bool &allRelations){ float m_weight=0; bool edgeStatus=false; H_edges::iterator it1=m_outEdges.find(v2); while (it1 != m_outEdges.end() && it1.key() == v2 ) { if (!allRelations) { if ( it1.value().first == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_weight=it1.value().second.first; qDebug()<< "Vertex::hasEdgeTo() - "<< this->name() << "->" << v2 << " = "<< m_weight; return m_weight; } else { qDebug()<< "Vertex::hasEdgeTo() - "<< this->name() << "->" << v2 << " = "<< m_weight << " but edgeStatus " << edgeStatus; return 0; } } } else { m_weight=it1.value().second.first; qDebug()<< "Vertex::hasEdgeTo() - "<< this->name() << "->" << v2 << " = "<< m_weight << "relation"<name() // << ", " << v2 << ") = "<< m_weight; return 0; } /** * @brief Vertex::hasEdgeFrom * Checks if this vertex is inLinked from v2 and returns the weight of the link * only if the inLink is enabled. * @param v2 * @return */ float Vertex::hasEdgeFrom(const long int &v2, const bool &allRelations){ float m_weight=0; bool edgeStatus=false; H_edges::iterator it1=m_inEdges.find(v2); while (it1 != m_inEdges.end() && it1.key() == v2) { if (!allRelations) { if ( it1.value().first == m_curRelation ) { edgeStatus=it1.value().second.second; if ( edgeStatus == true) { m_weight=it1.value().second.first; qDebug()<< "Vertex::hasEdgeFrom() - "<< this->name() << "<-" << v2 << " = "<< m_weight; return m_weight; } else { qDebug()<< "Vertex::hasEdgeFrom() - "<< this->name() << "<-" << v2 << " = "<< m_weight << " but edgeStatus " << edgeStatus; return 0; } } } else { m_weight=it1.value().second.first; qDebug()<< "Vertex::hasEdgeFrom() - "<< this->name() << "<-" << v2 << " = "<< m_weight << "relation"<name() << ", " << v2 << ") = 0 "; return 0; } /** * @brief Vertex::cliques * Returns the number of cliques sized size this vertex belongs to * @param size * @return */ int Vertex::cliques (const int &ofSize) { return m_cliques.values( ofSize ).size(); } /** * @brief Vertex::cliqueAdd * @param clique */ void Vertex::cliqueAdd (const QList &clique) { qDebug()<<"Vertex::cliqueAdd() - vertex:" << name() << "in a clique with:" << clique; m_cliques.insertMulti(clique.size(), clique); } /** * @brief Vertex::clearPs */ void Vertex::clearPs() { myPs.clear(); } void Vertex::appendToPs(long int vertex ) { qDebug() << "adding " << vertex << " to myPs"; myPs.append(vertex); } L_int Vertex::Ps(void) { return myPs; } Vertex::~Vertex() { qDebug() << " Vertex:: destroying my data"; m_outEdges.clear(); outLinkColors.clear(); m_outEdgeLabels.clear(); clearPs(); m_outEdges.clear(); m_inEdges.clear(); m_reciprocalEdges->clear(); } socnetv-2.2/src/vertex.h000755 000765 000024 00000027220 13040734202 016303 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt vertex.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef VERTEX_H #define VERTEX_H #include #include #include #include #include #include #include using namespace std; class QPointF; class Graph; typedef QHash H_IntToStr; typedef QList L_int; typedef QPair pair_f_b; typedef QPair rel_w_bool; typedef QHash < int, rel_w_bool > H_edges; typedef QHash H_StrToInt; class Vertex : public QObject{ Q_OBJECT public: Vertex(Graph* parent, const long int &name, const int &val, const int &relation, const int &size, const QString &color, const QString &numColor, const int &numSize, const QString &label, const QString &labelColor, const int &labelSize, const QPointF &p, const QString &shape); Vertex(const long int &name); ~Vertex(); long int name() const { return m_name; } void setName (const long int &name) { m_name=name; } void setEnabled (const bool &flag ) { m_enabled=flag; } bool isEnabled () const { return m_enabled; } void relationSet(int) ; void edgeAddTo (const long int &v2, const float &weight); void edgeAddFrom(const long int &v1, const float &weight); void changeOutEdgeWeight (long int target, float weight); void setOutEdgeEnabled (long int, bool); void edgeRemoveTo (long int target); void edgeRemoveFrom(long int source); QHash* outEdgesEnabledHash(const bool &allRelations=false); QHash* outEdgesAllRelationsUniqueHash(); QHash* inEdgesEnabledHash(); QHash* reciprocalEdgesHash(); QList neighborhoodList(); long int outEdges(); long int outEdgesConst() const ; long int inEdges(); long int inEdgesConst() const ; long int degreeOut(); long int outDegreeConst(); long int degreeIn(); long int inDegreeConst(); long int localDegree(); /* sets eccentricity */ void setEccentricity (float c){ m_Eccentricity=c;} float eccentricity() { return m_Eccentricity;} /* Returns true if there is an outLink from this vertex */ bool isOutLinked() { return (outEdges() > 0) ? true:false;} float hasEdgeTo(const long int &v, const bool &allRelations=false); /* Returns true if there is an outLink from this vertex */ bool isInLinked() { return (inEdges() > 0) ? true:false;} float hasEdgeFrom (const long int &v, const bool &allRelations=false); bool isIsolated() { return !(isOutLinked() | isInLinked()) ; } void setIsolated(bool isolated) {m_isolated = isolated; } void edgeFilterByWeight(float m_threshold, bool overThreshold); // void filterEdgesByColor(float m_threshold, bool overThreshold); void edgeFilterByRelation(int relation, bool status); void edgeFilterUnilateral(const bool &toggle=false); void setSize(const int &size ) { m_size=size; } int size() const { return m_size; } void setShape(const QString &shape) { m_shape=shape; } QString shape() const { return m_shape; } void setColor(const QString &color) { m_color=color; } QString color() const { return m_color; } QString colorToPajek(); void setNumberColor (const QString &color) { m_numberColor = color; } QString numberColor() const { return m_numberColor; } void setNumberSize (const int &size) { m_numberSize=size; } int numberSize() const { return m_numberSize; } void setNumberDistance (const int &distance) { m_numberDistance=distance; } int numberDistance() const { return m_numberDistance; } void setLabel (const QString &label) { m_label=label; } QString label() const { return m_label; } void setLabelColor (const QString &labelColor) { m_labelColor=labelColor; } QString labelColor() const { return m_labelColor; } void setLabelSize(const int &size) { m_labelSize=size; } int labelSize() const { return m_labelSize; } void setLabelDistance (const int &distance) { m_labelDistance=distance; } int labelDistance() const { return m_labelDistance; } void setX(const float &x) { m_x=x; } float x() const { return m_x; } void setY(const float &y) { m_y=y; } float y() const { return m_y; } QPointF pos () const { return QPointF ( x(), y() ); } void setPos (QPointF &p) { m_x=p.x(); m_y=p.y(); } //returns displacement vector QPointF & disp() { return m_disp; } void set_dispX (float x) { m_disp.rx() = x ; } void set_dispY (float y) { m_disp.ry() = y ; } void setOutLinkColor(const long int &v2, const QString &color) { outLinkColors[v2]=color; } QString outLinkColor(const long int &v2) { return ( outLinkColors.contains(v2) ) ? outLinkColors.value(v2) : "black"; } void setOutEdgeLabel(const long int &v2, const QString &label) { m_outEdgeLabels[v2]=label; } QString outEdgeLabel(const long int &v2) const { return ( m_outEdgeLabels.contains(v2) ) ? m_outEdgeLabels.value(v2) : QString::null; } void setDelta (float c){ m_delta=c;} /* Sets vertex pair dependancy */ float delta() { return m_delta;} /* Returns vertex pair dependancy */ void clearPs() ; void appendToPs(long int vertex ) ; L_int Ps(void); void setDC (float c){ m_DC=c;} /* Sets vertex Degree Centrality*/ void setSDC (float c ) { m_SDC=c;} /* Sets standard vertex Degree Centrality*/ float DC() { return m_DC;} /* Returns vertex Degree Centrality*/ float SDC() { return m_SDC;} /* Returns standard vertex Degree Centrality*/ void setCC (float c){ m_CC=c;} /* sets vertex Closeness Centrality*/ void setSCC (float c ) { m_SCC=c;} /* sets standard vertex Closeness Centrality*/ float CC() { return m_CC;} /* Returns vertex Closeness Centrality*/ float SCC() { return m_SCC; } /* Returns standard vertex Closeness Centrality*/ void setIRCC (float c){ m_IRCC=c;} /* sets vertex IRCC */ void setSIRCC (float c ) { m_SIRCC=c;} /* sets standard vertex IRCC */ float IRCC() { return m_IRCC;} /* Returns vertex IRCC */ float SIRCC() { return m_SIRCC; } /* Returns standard vertex IRCC*/ void setBC(float c){ m_BC=c;} /* sets s vertex Betweenness Centrality*/ void setSBC (float c ) { m_SBC=c;} /* sets standard vertex Betweenness Centrality*/ float BC() { return m_BC;} /* Returns vertex Betweenness Centrality*/ float SBC() { return m_SBC; } /* Returns standard vertex Betweenness Centrality*/ void setSC (float c){ m_SC=c;} /* sets vertex Stress Centrality*/ void setSSC (float c ) { m_SSC=c;} /* sets standard vertex Stress Centrality*/ float SC() { return m_SC;} /* Returns vertex Stress Centrality*/ float SSC() { return m_SSC; } /* Returns standard vertex Stress Centrality*/ void setEC(float dist) { m_EC=dist;} /* Sets max Geodesic Distance to all other vertices*/ void setSEC(float c) {m_SEC=c;} float EC() { return m_EC;} /* Returns max Geodesic Distance to all other vertices*/ float SEC() { return m_SEC;} void setPC (float c){ m_PC=c;} /* sets vertex Power Centrality*/ void setSPC (float c ) { m_SPC=c;} /* sets standard vertex Power Centrality*/ float PC() { return m_PC;} /* Returns vertex Power Centrality*/ float SPC() { return m_SPC; } /* Returns standard vertex Power Centrality*/ void setIC (float c){ m_IC=c;} /* sets vertex Information Centrality*/ void setSIC (float c ) { m_SIC=c;} /* sets standard vertex Information Centrality*/ float IC() { return m_IC;} /* Returns vertex Information Centrality*/ float SIC() { return m_SIC; } /* Returns standard vertex Information Centrality*/ void setDP (float c){ m_DP=c;} /* Sets vertex Degree Prestige */ void setSDP (float c ) { m_SDP=c;} /* Sets standard vertex Degree Prestige */ float DP() { return m_DP;} /* Returns vertex Degree Prestige */ float SDP() { return m_SDP;} /* Returns standard vertex Degree Prestige */ void setPRP (float c){ m_PRC=c;} /* sets vertex PageRank*/ void setSPRP (float c ) { m_SPRC=c;} /* sets standard vertex PageRank*/ float PRP() { return m_PRC;} /* Returns vertex PageRank */ float SPRP() { return m_SPRC; } /* Returns standard vertex PageRank*/ void setPP (float c){ m_PP=c;} /* sets vertex Proximity Prestige */ void setSPP (float c ) { m_SPP=c;} /* sets standard vertex Proximity Prestige */ float PP() { return m_PP;} /* Returns vertex Proximity Prestige */ float SPP() { return m_SPP; } /* Returns standard vertex Proximity Prestige */ float CLC() { return m_CLC; } void setCLC(float clucof) { m_CLC=clucof; m_hasCLC=true; } bool hasCLC() { return m_hasCLC; } void setEVC (float c){ m_EVC=c;} /* Sets vertex Degree Centrality*/ void setSEVC (float c ) { m_SEVC=c;} /* Sets standard vertex Degree Centrality*/ float EVC() { return m_EVC;} /* Returns vertex Degree Centrality*/ float SEVC() { return m_SEVC;} /* Returns standard vertex Degree Centrality*/ int cliques (const int &ofSize); void cliqueAdd (const QList &clique); void clearCliques() { m_cliques.clear(); } //hold all outbound and inboud edges of this vertex. H_edges m_outEdges, m_inEdges; signals: void setEdgeVisibility (int, int, int, bool); protected: private: Graph *parentGraph; long int m_name, m_outEdgesCounter, m_inEdgesCounter, m_outDegree, m_inDegree, m_localDegree; int m_value, m_size, m_labelSize, m_numberSize, m_numberDistance, m_labelDistance; int m_curRelation; bool m_reciprocalLinked, m_enabled, m_hasCLC, m_isolated; double m_x, m_y; float m_Eccentricity, m_CLC; float m_delta, m_EC, m_SEC; float m_DC, m_SDC, m_DP, m_SDP, m_CC, m_SCC, m_BC, m_SBC, m_IRCC, m_SIRCC, m_SC, m_SSC; float m_PC, m_SPC, m_SIC, m_IC, m_SPRC, m_PRC; float m_PP, m_SPP, m_EVC, m_SEVC; QString m_color, m_numberColor, m_label, m_labelColor, m_shape; QPointF m_disp; QHash* m_reciprocalEdges; L_int myPs; L_int m_neighborhoodList; H_IntToStr outLinkColors, m_outEdgeLabels; QHash m_cliques; //FIXME vertex coords }; #endif socnetv-2.2/src/webcrawler.cpp000755 000765 000024 00000043206 13040734201 017457 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt webcrawler.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include "webcrawler.h" #include #include #include #include /* * frontier is our Url buffer (global) */ QQueue frontier; /* * constructor called from parent thread * - Inits variables * - Connects http NetworkManager signal to httpfinished() slot * which in turn emits http reply to WebCrawler_Parser */ WebCrawler_Spider::WebCrawler_Spider(QString url, int maxN, int maxLinksPerPage, bool extLinks, bool intLinks) { qDebug() << " wc_spider::WebCrawler_Spider() " << " this->thread() " << this->thread() << " Initializing vars ..."; m_seed=url; //the initial url/domain we will crawl m_maxPages=maxN; //maxPages we'll check m_maxLinksPerPage = maxLinksPerPage; m_extLinks = extLinks; m_intLinks = intLinks; m_visitedNodes = 0; qDebug() << " wc_spider::WebCrawler_Spider() Creating http"; http = new QNetworkAccessManager(this); qDebug() << " wc_spider::WebCrawler_Spider() Connecting http signals"; connect ( http, &QNetworkAccessManager::finished, this, &WebCrawler_Spider::httpFinished ); qDebug () << " wc_spider::WebCrawler_Spider() http->thread() " << http->thread() ; } WebCrawler_Spider::~WebCrawler_Spider() { m_visitedNodes = 0; delete http; } /* * WebCrawer_Spider main functionality * Parses urls from frontier and downloads their data. * When http signals finished() the response data are passed to * wc_parser thread to parse them */ void WebCrawler_Spider::get(){ qDebug() << " wc_spider::get():"; do { //repeat forever.... // or until we crawl all urls in frontier. if (frontier.size() ==0 ) { qDebug () <<" wc_spider::get() #### Frontier is empty: " <0) { if (m_visitedNodes == m_maxPages ) { qDebug () <<" wc_spider::get(): #### Reached m_maxPages!" << " - STOP" ; emit finished("maxpages from spider"); break; } } qDebug() << " wc_spider::get():" <<" frontier size " << frontier.size() << " - Take the first url from frontier "; currentUrl = frontier.dequeue(); qDebug() << " wc_spider::get(): currentUrl: " << currentUrl; qDebug() << " wc_spider::get(): currentUrl not visited." << " Increasing visitedNodes to: " << m_visitedNodes + 1 << " Let us visit it."; qDebug() << " wc_spider::get(): currentUrl: " << currentUrl << " downloading html "; request = new QNetworkRequest; request->setUrl(currentUrl); request->setRawHeader( "User-Agent", "SocNetV innocent spider 1 - see http://socnetv.sf.net"); qDebug() << " wc_spider::get(): http->get() "; qDebug() << " wc_spider::get() http->thread() " << http->thread() ; QNetworkReply *reply = http->get(*request) ; Q_UNUSED(reply); m_visitedNodes++; } while ( 1 ); qDebug() << " wc_spider::get() Finished!"; } void WebCrawler_Spider::httpFinished(QNetworkReply *reply){ qDebug() << " wc_spider::httpFinished()"; emit parse (reply); } //constructor called from parent thread - does nothing WebCrawler_Parser::WebCrawler_Parser(QString url, int maxN, int maxLinksPerPage, bool extLinks, bool intLinks) { qDebug () << " wc_parser::WebCrawler_Parser() this->thread() " << this->thread() << " Initializing variables "; m_seed=QUrl (url); //the initial url/domain we will crawl m_maxPages=maxN; //maxPages we'll check m_maxLinksPerPage = maxLinksPerPage; m_extLinks = extLinks; m_intLinks = intLinks; //clear global variables frontier.clear(); ba.clear(); knownUrls.clear(); m_discoveredNodes=0; m_discoveredNodes++; frontier.enqueue(m_seed); knownUrls[m_seed]=m_discoveredNodes; qDebug() << " wc_parser::WebCrawler_Parser() seed: " << m_seed << " seed_domain: " << m_seed.host() << " Added seed to frontier queue and knownUrls map. " << " Node " << m_discoveredNodes << " should be already created. " << " m_maxPages " << m_maxPages << " m_maxLinksPerPage " << m_maxLinksPerPage << " m_extLinks " << m_extLinks << " m_intLinks " << m_intLinks; } WebCrawler_Parser::~WebCrawler_Parser() { ba.clear(); frontier.clear(); knownUrls.clear(); m_discoveredNodes = 0; } /* * This method is called when http has finished all pending requests. * First, we start by reading all from http to the QString page. * Then we parse the page string, searching for url substrings. */ void WebCrawler_Parser::parse(QNetworkReply *reply){ qDebug () << " wc_parser::parse() thread " << this->thread(); // find to which node the response HTML belongs to // Get this from the reply object request method QUrl requestUrl = reply->request().url(); QString requestUrlStr = requestUrl.toString(); QString locationHeader = reply->header(QNetworkRequest::LocationHeader).toString(); int sourceNode = knownUrls [ requestUrl ]; QString host = requestUrl.host(); QString path = requestUrl.path(); qDebug() << " wc_parser::parse() response HTML of url " << requestUrlStr << " sourceNode " << sourceNode << " host " << host << " path " << path; qDebug () << " wc_parser::parse(): original locationHeader" << reply->header(QNetworkRequest::LocationHeader) ; qDebug () << " wc_parser::parse(): decoded locationHeader" << locationHeader ; qDebug () << " wc_parser::parse(): encoded requestUrl " << requestUrl; qDebug () << " wc_parser::parse(): decoded requestUrl " << requestUrlStr; if ( locationHeader != "" && locationHeader != requestUrlStr ) { qDebug () << "&& wc_parser::parse() Location response header " << locationHeader << " differs from requestUrl " << requestUrlStr << " Creating node redirect - Creating edge - RETURN "; newLink( sourceNode, locationHeader , true ); return; } QUrl newUrl; QString newUrlStr; int start=-1, end=-1, equal=-1 , invalidUrlsInPage =0; // index=-1; int validUrlsInPage = 0; ba=reply->readAll(); QString page(ba); if (!page.contains ("href")) { //if a href doesnt exist, return //FIXME: Frameset pages are not parsed! See docs/manual.html for example. qDebug() << " wc_parser::parse(): ### Empty or not useful html from " << requestUrl << " page size " << page.size() << " \npage contents: " << page << " RETURN"; return; } qDebug() << " wc_parser::parse(): erasing "; start=page.indexOf (""); //Find head pos if ( start != -1 && end != -1 ) { page = page.remove(start, end); //erase head } else if ( start == -1 ) { qDebug() << " wc_parser::parse(): ERROR IN locating tag start"; } else if ( end == -1 ) { qDebug() << " wc_parser::parse(): ERROR IN locating tag end"; } while (page.contains(""); //Find pos if ( start != -1 && end != -1 ) { page = page.remove(start, end); //erase head } } while (page.contains("href")) { //as long there is a href in the page... if (m_maxPages>0) { if (m_discoveredNodes >= m_maxPages ) { qDebug () <<"!! wc_parser::parse(): Reached maxPages! STOP "; emit finished("maxpages from parse()"); return; } } // remove whitespace from the start and the end // all whitespace sequence becomes single space page=page.simplified(); start=page.indexOf ("href"); //Find its pos page = page.remove(0, start); //erase everything up to href equal=page.indexOf ("="); // Find next equal sign (=) page = page.remove(0, equal+1); //Erase everything up to = if (page.startsWith("\"") ) { page.remove(0,1); end=page.indexOf ("\""); } else if (page.startsWith("\'") ){ page.remove(0,1); end=page.indexOf ("\'"); } else { //end=page.indexOf ("\'"); } newUrlStr=page.left(end); //Save new url to newUrl :) newUrlStr=newUrlStr.simplified(); newUrl = QUrl(newUrlStr); if (!newUrl.isValid()) { invalidUrlsInPage ++; qDebug() << " wc_parser::parse(): found INVALID newUrl " << newUrl.toString() << " in page " << requestUrlStr << " Will CONTINUE only if invalidUrlsInPage < 200"; if (invalidUrlsInPage > 200) { qDebug() << " wc_parser::parse(): INVALID newUrls > 200"; emit finished("invalidUrlsInPage > 200"); return; } continue; } qDebug() << "@@ wc_parser::parse(): found VALID newUrl: " << newUrlStr << " in page " << requestUrlStr << " decoded newUrl " << newUrl.toString(); newUrlStr = newUrl.toString(); // ...skip css, favicon, rss, ping, etc if ( newUrlStr.startsWith("#", Qt::CaseInsensitive) || newUrlStr.endsWith("feed/", Qt::CaseInsensitive) || newUrlStr.endsWith("rss/", Qt::CaseInsensitive) || newUrlStr.endsWith("atom/", Qt::CaseInsensitive) || newUrl.fileName().endsWith("xmlrpc.php", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".xml", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".ico", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".gif", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".png", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".jpg", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".js", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".css", Qt::CaseInsensitive) || newUrl.fileName().endsWith(".rsd", Qt::CaseInsensitive) ) { qDebug()<< "!! wc_parser::parse(): # newUrl " << " seems a page resource or anchor (rss, favicon, etc) " << "Skipping..."; continue; } if ( newUrl.isRelative() ) { newUrl = requestUrl.resolved(newUrl); newUrlStr = newUrl.toString(); qDebug() << " wc_parser::parse(): isRelative TRUE" << " host: " << host << " resolved url " << newUrl.toString(); if (!m_intLinks ){ qDebug()<< " wc_parser::parse(): m_IntLinks = FALSE" << " SKIPPING node creation"; continue; } if (requestUrl.path() == newUrl.path()) { qDebug()<< " wc_parser::parse(): m_IntLinks = TRUE" << " requestUrl.path() = newUrl.path()" << " Creating self link only"; this->newLink(sourceNode, newUrl, false); } else { qDebug()<< " wc_parser::parse(): m_IntLinks = TRUE" << " Creating new node and ADDING it to frontier..."; this->newLink(sourceNode, newUrl, true); } } else { qDebug() << " wc_parser::parse(): isRelative FALSE"; if ( newUrl.scheme() != "http" && newUrl.scheme() != "https" && newUrl.scheme() != "ftp" && newUrl.scheme() != "ftps") { qDebug() << " wc_parser::parse(): found INVALID newUrl SCHEME" << newUrl.toString(); continue; } if ( newUrl.host() != host ) { qDebug()<< " wc_parser::parse(): absolute newUrl " << " is EXTERNAL "; if ( !m_extLinks ) { qDebug()<< " wc_parser::parse(): m_extLinks = false . " <<" Creating new node but NOT ADDING it to frontier..."; this->newLink(sourceNode, newUrl, false); } else { qDebug()<< " wc_parser::parse(): m_extLinks = true " <<" Creating new node and ADDING it to frontier..."; this->newLink(sourceNode, newUrl, true); } } else { qDebug()<< " wc_parser::parse(): absolute newUrl" << " is INTERNAL "; if (!m_intLinks){ qDebug()<< " wc_parser::parse(): m_IntLinks = FALSE" << " SKIPPING node creation"; continue; } qDebug()<< " wc_parser::parse(): m_IntLinks = TRUE" <<" Creating new node and ADDING it to frontier..."; this->newLink(sourceNode, newUrl, true); } } validUrlsInPage ++; qDebug() << " wc_parser::parse(): validUrlsInPage " << validUrlsInPage; // or until we reach maxRecursion if ( m_maxLinksPerPage != 0 ) { if ( validUrlsInPage > m_maxLinksPerPage ) { qDebug () <<"!! wc_spider::parse() Reached m_maxLinksPerPage " <0) { if (m_discoveredNodes >= m_maxPages ) { qDebug () <<" wc_parser::newLink(): #### Seems we have reached maxPages!" << " - STOP!" ; emit finished("maxpages from newLink"); return; } } // check if the new url has been discovered previously QMap::const_iterator index = knownUrls.find(target); if ( index!= knownUrls.end() ) { qDebug()<< "-- wc_parser::newLink(): target already discovered " << " in knownUrls. Creating edge from " << s << " to " << index.value(); emit signalCreateEdge (s, index.value() ); return; } m_discoveredNodes++; knownUrls[target]=m_discoveredNodes; emit signalCreateNode( m_discoveredNodes, target.toString(), false); qDebug()<< "** wc_parser::newLink(): Creating node " << m_discoveredNodes << " url "<< target.toString(); if (enqueue_to_frontier) { frontier.enqueue(target); qDebug()<< "** wc_parser::newLink(): Enqueuing new node to frontier " << " frontier size: "<< frontier.size(); emit startSpider(); } else { qDebug()<< "## wc_parser::newLink(): NOT enqueuing to frontier"; } qDebug()<< "-- wc_parser::newLink(): Creating edge from " << s << " to " << m_discoveredNodes; emit signalCreateEdge (s, m_discoveredNodes); } socnetv-2.2/src/webcrawler.h000755 000765 000024 00000006112 13040734202 017120 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt webcrawler.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef WEBCRAWLER_H #define WEBCRAWLER_H #include #include class QNetworkAccessManager; class QNetworkRequest; using namespace std; class WebCrawler_Parser : public QObject { Q_OBJECT public: WebCrawler_Parser(QString seed, int maxNodes, int maxLinksPerPage, bool extLinks, bool intLinks); ~WebCrawler_Parser(); public slots: void parse(QNetworkReply *reply); void newLink(int s, QUrl target, bool enqueue_to_frontier); signals: void signalCreateNode(const int &no, const QString &url, const bool &signalMW=false); void signalCreateEdge (const int &source, const int &target); void startSpider(); void finished (QString); private: QByteArray ba; QMap knownUrls; QUrl m_seed; int m_maxPages; int m_discoveredNodes; int m_maxLinksPerPage; bool m_extLinks, m_intLinks; }; class WebCrawler_Spider : public QObject { Q_OBJECT public: WebCrawler_Spider(QString seed, int maxNodes, int maxLinksPerPage ,bool extLinks, bool intLinks); ~WebCrawler_Spider(); public slots: void get(); void httpFinished(QNetworkReply *reply); signals: void parse(QNetworkReply *reply); void finished (QString); private: QNetworkAccessManager *http; QNetworkRequest *request; QNetworkReply *reply; QUrl currentUrl ; QString m_seed; int m_maxPages; int m_visitedNodes; int m_maxLinksPerPage; bool m_extLinks, m_intLinks; }; #endif socnetv-2.2/src/webcrawlerdialog.cpp000755 000765 000024 00000011757 13040734201 020645 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt webcrawlerdialog.cpp - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #include "webcrawlerdialog.h" #include #include #include WebCrawlerDialog::WebCrawlerDialog(QWidget *parent) : QDialog (parent) { ui.setupUi(this); connect ( ui.buttonBox,SIGNAL(accepted()), this, SLOT(gatherData()) ); (ui.buttonBox) -> button (QDialogButtonBox::Ok) -> setDefault(true); (ui.seedUrlEdit)->setFocus(); connect (ui.extLinksCheckBox, &QCheckBox::stateChanged, this, &WebCrawlerDialog::checkErrors); connect (ui.intLinksCheckBox, &QCheckBox::stateChanged, this, &WebCrawlerDialog::checkErrors); if ( !ui.extLinksCheckBox->isChecked() &&!ui.intLinksCheckBox->isChecked() ) { (ui.buttonBox) -> button (QDialogButtonBox::Ok)->setDisabled(true); } } void WebCrawlerDialog::checkErrors(){ qDebug()<< "WebCrawlerDialog::checkErrors..."; if ( !ui.extLinksCheckBox->isChecked() && !ui.intLinksCheckBox->isChecked() ) { (ui.buttonBox) -> button (QDialogButtonBox::Ok)->setDisabled(true); } else (ui.buttonBox) -> button (QDialogButtonBox::Ok)->setEnabled(true); } void WebCrawlerDialog::gatherData(){ qDebug()<< "WebCrawlerDialog::gatherData()..."; bool extLinks=true, intLinks=false; QString seedUrl = (ui.seedUrlEdit)->text(); qDebug()<< "WebCrawlerDialog::gatherData() initial seed url " << seedUrl << " simplifying and lowering it"; seedUrl = seedUrl.simplified().toLower() ; qDebug()<< "WebCrawlerDialog::gatherData() adding / to seed url "; seedUrl = seedUrl + "/"; QUrl newUrl(seedUrl); qDebug()<< "WebCrawlerDialog::gatherData() QUrl " << newUrl.toString() << " scheme " << newUrl.scheme() << " host " << newUrl.host() << " path " << newUrl.path(); if ( newUrl.scheme() != "http" && newUrl.scheme() != "https" && newUrl.scheme() != "ftp" && newUrl.scheme() != "ftps") { qDebug()<< "WebCrawlerDialog::gatherData() URL scheme missing " << newUrl.scheme() << " setting the default scheme http "; newUrl = QUrl ("http://" + seedUrl); qDebug() << newUrl; } if (! newUrl.isValid() || newUrl.host() == "") { emit webCrawlerDialogError(seedUrl); qDebug()<< "WebCrawlerDialog::gatherData() not valid URL"; return; } seedUrl = newUrl.toString(); qDebug()<< "WebCrawlerDialog::gatherData() final seed url " << newUrl << " scheme " << newUrl.scheme() << " host " << newUrl.host() << " path " << newUrl.path(); int maxLinksPerPage = (ui.maxLinksPerPageSpinBox) -> value(); int totalUrlsToCrawl = (ui.totalUrlsToCrawlSpinBox) -> value(); if ( ui.extLinksCheckBox -> isChecked() ) { qDebug()<< " External links will be crawled... " ; extLinks = true; } else { qDebug()<< " No external links... "; extLinks = false; } if ( ui.intLinksCheckBox -> isChecked() ) { qDebug()<< " Internal links will be crawled too. " ; intLinks = true; } else { qDebug()<< " No internal links. "; intLinks = false; if (!intLinks && !extLinks) return; } qDebug()<< " seedUrl: " << seedUrl; qDebug()<< " maxLinksPerPage " << maxLinksPerPage << " totalUrlsToCrawl " << totalUrlsToCrawl ; emit userChoices( seedUrl, totalUrlsToCrawl, maxLinksPerPage, extLinks, intLinks ); } socnetv-2.2/src/webcrawlerdialog.h000755 000765 000024 00000004103 13040734202 020276 0ustar00dimitrisstaff000000 000000 /*************************************************************************** SocNetV: Social Network Visualizer version: 2.2 Written in Qt webcrawlerdialog.h - description ------------------- copyright : (C) 2005-2017 by Dimitris B. Kalamaras email : dimitris.kalamaras@gmail.com website: : http://dimitris.apeiro.gr project site : http://socnetv.org ***************************************************************************/ /******************************************************************************* * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ********************************************************************************/ #ifndef WEBCRAWLERDIALOG_H #define WEBCRAWLERDIALOG_H #include #include "ui_webcrawlerdialog.h" class WebCrawlerDialog: public QDialog { Q_OBJECT public: WebCrawlerDialog (QWidget *parent = 0); public slots: void checkErrors (); void gatherData (); signals: void userChoices( QString, int, int, bool, bool); void webCrawlerDialogError(QString); private: Ui::WebCrawlerDialog ui; }; #endif socnetv-2.2/src/images/add.png000644 000765 000024 00000004061 13040701535 017316 0ustar00dimitrisstaff000000 000000 PNG  IHDRĴl;sRGBbKGD pHYs ,tIME2)'IDAT8Y7*;a/ -"Є(L;?IMJ:(B5P R   R9, qL;4 P3!+$   @*- >)5(  "   .    8! bQc 2SO@    <'wSA&-++ cq+ !$M)  E1 -*!T# XF2%< ;Ⱦ&086F! ׻: #"!^Ƣո?cšžR x0IENDB`socnetv-2.2/src/images/addrelation.png000644 000765 000024 00000001411 13040701535 021050 0ustar00dimitrisstaff000000 000000 PNG  IHDR D&PLTEѯѢ½½þe[tRNSo_}IDATxyS@n;;+5-e%@xo+3L3}TzDf([TJӂ@c(Xʲ\TdQpWU\ŧ:w' ZMh4j`"=6h7qWm_91VMt0VaTMԄAmq= ر4Y4p„`K م`P@ 7!p !:t:1UgRJ =܆ Řmۦ9(M|AGQAnw>zeqƘzO!4 M$놧(? qA0.# 8! I (XTgIENDB`socnetv-2.2/src/images/adjacencyplot.png000644 000765 000024 00000000254 13040701535 021406 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzsIDATXc` ?:8뚨185ԏ:E`@Z5.@Q8 <`פ:E`4 x.e\2( Gsl|rmFv.H [NL3wIENDB`socnetv-2.2/src/images/appsettings.png000644 000765 000024 00000006371 13040701535 021135 0ustar00dimitrisstaff000000 000000 PNG  IHDR@@iqbKGD pHYs  tIME #,+e IDATx[}p\u$˖+d!ޮ661؄|81 2c0-ôi$Mi'tʤiZId)IӁN4 @iIgb ,bw߯[SZʙjs{>YA(8 ] BL<_Yq xZ_O8E6L-tZI &ލFWuMb'D&ߘWE<9lҶ ClܸQM#p c"<|0SK_!yWX?k"DžJtdm X*dVZƉ([+ _)rgL 8}~]o9 P#>9z?!ƽG!npMŊ,_< j`ྔt Mr 4+}teQ[C2jmHg!4M 9KpHdY :Z#cz*PF7A)#,Ju,ԔĺֻF99 n*P2Ve5@E:Mz_A;8<Zdi\J!/I$&w9Ǭ]+011Db9?rC5-UDO"*|7x*c)Okk|=þ_l 7_ȣw]& WNKժ2Euƾڕ ҆6+#$0dJDNG^' XQJ}8ن`,gH~ I__, @p1M T \n\…0Hg B+#@Jh}0a7Ii"# eYþ.GQm~B)P-.*z>O^R"{ 3y:9^rH~ sNߘZ#[%(<c=mΈȗݶ1/ZJMЈ:3  ij_\#qXiBacߘ|_`O6 h:f#v~ C:Pwµm<%-ؠW8Z2i/gDDn9Z-hIŶ!<ב_#śxV CuArdFuV{0|r!TO"QEK{FOww[O"eUzzd]wO^G8UW}c>]Ю@~4E@+"cX*Fzttt`(;YEx'ň E"}׮mb\3\Ce埑]X[e!7kY"y*ykӲ uW"@""Po͚RQeY0Y{z6*'Ǐכ0"4z]e2iݷT$O:NwiSVG^Cx*"; uxomVX%@$R5.~7䡔"c 6ӊs4) y% # &P(|;΢ +bJd JB+~k>iIEт i s<{l^AMϗl9/zZ_!FLXݶ ? |m|j9V "9iP"Ť{FGK3-5`MܫP)L<"`}}X먈|7[%75Ѳ{}O$eqH)uH6t@@{OOϏ TNk='y<|,[7FFҳ%ֹ\o s+ xu1\xW>RleYlOr Ft*uWleW]q1R)!i}2;FsuD 7SSPfEttv>788ެ(QApYy !2AbjqVGd/ q w0aS%>ie*iz$MR JkKljͥs2Jq`PK[WmMb7bƼ+F7=aIy\#~ߒ֛̒J(,](ꌀh]iN_ ]@ޔtU r"8("\/J}Pk{[)unA|>Պ]Jd|#g6J]u1twలP-Ql9g7Q-1/m??Czc>0J,I& O=|c[rDd@DƐ@Dyy)"_Bz#lm#cE "ONߘG@#Ɓ` pdy?l:-@ P /-6_B!;~Ɋ,xOYeP6׹a{hٶ|$ h4U$ @%  ,xV}P˶_  vNޘ ;J kaQM|x x@aM,|HB4aD>),)ܫJ*:>9ݷf<'ڗ|688koe %9`ɒ3Kf%3Df/x&^iUXR`[^@J3].#x-xHUOu]2n gY&]UUP22l:w; ޝŹmne 䃢*$(`ޣ2\Jun,أ(HO{-0 'x^`_޼yy]Z{{$sA6/_'x(0R[Kco,;v {$3I+ Q]]T%U-u'a|@,HN\@_&QPut*Y4c=^LPeZQ 68k79NG7 R덝PqS5_í4i B`.4oړ cƂ;w?tJ:&ʩ^zy#dWʖ nM-~-E<+hǐw~ϮP,h~z3--1[=ZiVSNJ1X,b蜼}=#Y0LS-W mH IENDB`socnetv-2.2/src/images/back.png000644 000765 000024 00000003131 13040701535 017463 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzbKGD pHYs ,tIME *IMIDATxŗ][Gwڬ#.Iv6P%6{D " )/-m +CߪTBJ""%* UԈ .]lM^3<ڎl 33yhx.PGA(ٴAw|tZ},L$X@:8K&+9K?0vfx%!A"8;%>@فFj,e8O[Io%|?'EDۚR}[L'5鴿ur= yx`MFh;<[gLn.,_XWH]ɍ;vle}nvl}QZ0rh>5P!gYdT3o0ϜTU̕9u$;\Z>5s$ >v]sCӗ ~u$?]oMr(C,)<}4 ل6sZƵNG{/P(`gJfҶ[f,rP 4-^8TX:GK6a{ˀm}]abe?Z lٕWWJ咋BfF'k'Z  2H*Yb{Ӎ?/u%^XqpEjn1bfA`jM(>VOrkWWs#-h@D=@ܥ|O=ݢ<^&] "؍$ȧ"F2Ru܍9Q=FsJxnk( v CzJCz18kQuz.ς@yY@aAө>ޚCg]7hM/]ˢw%em8^.]t a 7yčv|'={-X<AVOH_(pOG_9T_(mlRT[d2f_+oO $Čvq׬D旦ow_]o\{S&Pl>] @v>G荺mF]bGv>W7t/z ɨϫ Ǘ5vE:Ϳ9*5>)Pu>>.Ĥ#DϢcQSɭ IueMx Uj_Cw 6]PD T>{M]G WK>۴X-߇m (tښk{1""{I /KP2a wjV9 az2`p6jN JWIENDB`socnetv-2.2/src/images/box.png000644 000765 000024 00000000441 13040701535 017354 0ustar00dimitrisstaff000000 000000 PNG  IHDRVΎWbKGD pHYs+tIME 8%1tEXtCommentCreated with GIMPWIDAT8픱 @,9T3߀ ܁P*w!8΁mPo>`na IB8DT՞tM+%L < KN :&p4/Xg$h}rp iRS 3\IENDB`socnetv-2.2/src/images/bugs.png000644 000765 000024 00000003345 13040701535 017532 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzbKGD pHYs  tIME %l-nrIDATX]Tgk̜333. hUQ>Bw[6bؚLS1*-ƶx-V[V!JREmYvfafw9mMh' 7&>SLӼ~(v8h;j>۶~m|6i0n0G8 kjvvaJKF\xsem'r{wv*Z!8wc}0puIWfljjLfeY5/m"mN%ݓ044cך@txxx 뺥Mk{O '}dd@iXkgwɯqffضkX7܈m"=t15MA r`BZ=*O:%w!鴌R?چL,y*/f^/ p'N0==8x|jA)kf$uPohWkl ,.XIZNV- L]۾wMCYY|2%7_)WY#(NY9*ǰMC~ĶW^ RĊPWGkQEG" 蛸^yry :>0<)t^`Νm׀7Bn=?z_|u-dZ0=˹I2,PrZ|َp:/-ZD  vdr.SwR9JrFqeX,6R.o=}mP`amTEAF IqU8ݦCEf3v{ 1 ޅ♰Nb&$8'A;EJC@IMB&SpPGH7IENDB`socnetv-2.2/src/images/circle.png000644 000765 000024 00000000725 13040701535 020032 0ustar00dimitrisstaff000000 000000 PNG  IHDRVΎWbKGD pHYs+tIME 8HjtEXtCommentCreated with GIMPW=IDAT8˭Qj@EK8 BJ$+(nA%~+T nEKD6 ~A1F0c81%[SJaQeBps粖1WIx:tE8KHg)=)2xl69,n%pp'(,H]q߱l-Z`$mlT}n>VFiʎq.¢DP|ჵՒcfIBW77|FRņ<8Fk!pzIENDB`socnetv-2.2/src/images/circular.png000644 000765 000024 00000004100 13040701535 020364 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzsBIT|d pHYspUtEXtSoftwarewww.inkscape.org<IDATXyp?~YHBB=@ e+RY:X@TbN 8PdK2B(İC;# Z3w~=|={*?MxڙMC\,忶}s/L₱iC=brMMCD@TmFCtml '?SU'۞ϴ,5,K 4**4(yY"-]wԻ 24 ==zHqqeOY ˲Fq"ZQQQU0S^{F OݲEprd M@DBk 4hްkaѥ_~kGE5z2M_~m-msDo i>q@?,wjί^ѳ#"BFx0h 4ӟ> %b J3q3㉪RrNs~MO.jtya `\wXVTd=. 9uJo߳l=#&UD\٧OlZrd8 @ pEd SU|\]SO`f5,%%2LUED4:c8VD; p\ئGU@!qMS\.eĶ 2vTEEU*csCEiVykn>QGDU=E1}Us z`ФqZl^RRYZrzwg !"l >UśLD"`:|g[4H+ > ,]n+0{69kײqfJD`Z A+ar8(@DO~U7&Mc؛^Tt50 T 3Ğ\Th("Ȭua^-"|/_)"mRR";^δvψ `:te]RE4:Uz]XtcvIQ0m$ h&>7Eu7m@+R֍#DDnaI81QU:wj8sF.@|jDR~`40ƻXl_Θo?y{q9L<v+T[Ps 'xB#w=շOFF}FRÛgtԓ!b._ `p=cϟ1dNgte'tͥĄ63))rpee-MMzY䉉M|vӁpp BNlwu5vww+3F@"\b, jU^-G,=5wnK^^+UZSS $q.{>0 J2>|"vnv=!͇%.^ϞP!'_XxaGAbAͦݰ!br1>J!.Z]o5o2ӳ9:ݡ )11r^yZ&| ҥR.2"B*P)|JV03##ooDž`b[w[mq>[]011Sz^<}76~&I_SJG}_V\x띚ju7g,R%!dAˎK|8C#thbfݳgF5 ziO;cc2JM…bKOV[閎J15Y,.[2Ǜk`RiZMJ)|;@dL844tV\x<2lko?OR6&Z#  ir W'+?PB2?ςp0gb"0 mmG[&09y]Cq%Ωu;{uv){H`-MIALH}UUh!ت8Η66m* BeJp@7JŔu:PY*3ڏTw݄QB䓤e6S(CLHwRP4W>dvek42`<`FE!Ӕ,i egF D$_D"Zn |J^@=N=X1jtDkPfZZ'MJńmjmEYs ?aXlq%ecrbP(5!?|hb/ӺdIR%ۥRf665RJa 4:=:Pzxo|w47U%0!$z5_nˋabG^ߴ$xaXA LK˜q1"ƙ3{vԌm\>`-xtNBeUꎅӆANwPjUܹsvQqܶXj7(kIENDB`socnetv-2.2/src/images/cliquenew.png000644 000765 000024 00000002720 13040701535 020562 0ustar00dimitrisstaff000000 000000 PNG  IHDR@?PbKGD pHYs  tIME .8tEXtCommentCreated with GIMPW8IDAThKoEkcc 1  "$BqAHHpDQQDB8p! $!ʂ$vbosfzz^KZlWԣ{հi_eO/%`}8zLڕ n0&8~fTOˆeNJ-K U-WȢSR([gRGn ~@ XjULrQ& ̵֡B;[Z{VUWUJ@]ᨂkMFY%S(+)oײ~t.>PȪN>TЭ5`U ,*a*p2՚CLf1 Ao֬(N7ӫK[˚EWێH紦ck3eGAQ|ڋ)!i0S~lFjהۂ Õ ?׬L '<#}z6ꅠA4LDN9CB"G>-&7/?! }##!uYe\!o蒏04}$!\MP9ʔ0Ή nK:a9 \$ݳ Sq:p[7duY&. M au+j.nזeV-6#{W`0ʧqT +Pe@"@Vݿ: L6NImp8# HVϕݭan@ 466E?Eb0qȠ^%8 #xZ%SB7Gyv<$Kgҩ+3$~">X@*]HL%]u=r=BLv !whq`ml/j`8B Ht@h80/!i 'm v+ 239OT_/𐅠%a:\ȎN-z8[(Z{|Nr&T~S eF/0\od-nKaշcdG%شCܯeouO(2ѢLj9Ͻh!s|!U"yPPR 2_7FiA^g7" :u͞|PD 4"kwd-h^]7-{]Wwo?wCd%atR;m^m rI"rU,+&:eވMuʼ'yPlEd]>K|2hhZ:#QIENDB`socnetv-2.2/src/images/clucof.png000644 000765 000024 00000000650 13040701535 020041 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzbKGD pHYs  tIME :2y5IDATXV 0 <>?$tQ9oidHC~Dr윏'{&F\XUg_ ΀07TTM UACZ€ot31,аPQ`iC PsYjBut.뼘}pm !4|Ɠ⤹kqAߵ]=;)kcRL$]@R%iv^Z@ڼo'h~$1 J(XN[Xs7w-|WcX-01`lIb_@ۡJhIENDB`socnetv-2.2/src/images/clustering.png000644 000765 000024 00000000362 13040701535 020745 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzIDATX0 CpH"R1|x,"I RzH5`CXL&8VPmOtFp '`n$WAūQ=4ߔ&(kynukHX\k-$@|OԐu:3C0jIENDB`socnetv-2.2/src/images/cocitation.png000644 000765 000024 00000000354 13040701535 020723 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzIDATXA {O=jMzYHE"Zӣㆧ@T4m"x_jBz  @/:2>u:Z!Uaa!LZ`2~Vo6` [0kd! D<`FQP2R(9ʀe" LYqφ MIENDB`socnetv-2.2/src/images/color.png000644 000765 000024 00000002665 13040701535 017714 0ustar00dimitrisstaff000000 000000 PNG  IHDRĴl; pHYs  gAMA|Q cHRMz%u0`:o_F+IDATxb?-0@ ,x.G,e۲ draf#31ġ#"92"9hADn>bH=uGgg 6?pC_fg% LLL(`BBB  @ ۶m޽ssf  @` &*|ԩ_7o\MM@syA @pA*Ι3?p 3իW/\𿭭IINX0222``pm;zΝ/_xӧO?~Ia h`, r-#%K cǟ??{ݻwϚ5yO>^^11j@ʪĵk?#Lap&%?ܞ=_#oҥg̘gϞ578;!+_q˗_z5إ  d@@YO : Yc\ΉūV_L ˁ̟?S !//o(`q{s6fd  ]pu[_i@WX 2e &#򿊊JP`YscΝ7zn[@.] ZP tDmm`qpd.@ ff0'ڵk3(#,[ l… 2t"0x@·l/Pq@`vc ʐr쮀2(,Zܹs SKAWUUE$@ 600l ɉ ""M6yd+a" RmllqY2 \2 1000p00\~R7y l`Vn$*>Ͽs/34 GIXzMR *d+ }c` {6>cS ~o;@wejo V! quS&g,>>}G ^˶>9(okX>/~`O)I{o~f,qH2%o9!ba`KQ򊗾kO d8Ozܴ:Z?0b` ,!1 k320 h,!Nq]3]KWV)_ӀAu:߳?bc`fV` X9~b`a` u 6`X~z .` @,rʼ 2(81 }DU們0B0Ȓ| 6_N 0}\g`S ^2z<07`p<B_o@0rPA-+@`xaE9Č ׵ $>q)s񲂃? $3fX3xAN ^q _3D<óE3oo~{=TN'㟿 oz1<{3Ó =d0Zq a3|S 3eX @j#`x(+05'vOq-//2H2 ^``Z.Çfc8CϿ @ b_- r32q^fA{ j`ed T{X./BT#)2'39td`gca`?SGRPm~ 5",CIENDB`socnetv-2.2/src/images/connect.png000644 000765 000024 00000001513 13040701535 020216 0ustar00dimitrisstaff000000 000000 PNG  IHDR ꂣAsBIT|d pHYs-5tEXtSoftwarewww.inkscape.org<IDATH]HQ1#[8 ISë "C* $/ nnh H- )dӧ ?Pn8y㜣DTRʨΥN/_ v:v ^(n"xM o^==(*B*T?,Bh}Pk+ ݵ..JK 9lشfYYL&L33|'oiypk_66vb]Jj|{;pf`AQJ]3\[K_u5FrX#0=M pT8)"2R෈hmxeH\v%`@x ֤ӉP ^9YH88IY>R|` cteH&sGs} `1,])ڀ@RJ8ds%m,F] *v0QlةRJUTUVͺ:r 49w|;0ȑlO9i_z+>ȣlcj+m$ZW҇_Z'9>8H_]ӭ5"{#5W=v'F9 ]Vu62RWF$բ+9{x-kxx k#Wנ6xx gEk`7Ƨikֽpi%yϝG4(rN0נxZn%gW';XJ @54TXFb@spѬk61 (k61 (kI1~5+m/_m_͊F"2Uki1 fx_ ,Hwq#uSǚf|gDY;7'Za|'@؀q%IENDB`socnetv-2.2/src/images/delete.png000644 000765 000024 00000001477 13040701535 020040 0ustar00dimitrisstaff000000 000000 PNG  IHDRĴl;gAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDAT8}KhSAI([5ZPڅ]v)-BIW΍ ຫ+QDE UV#Ŷj4ndN>?fc<&~Pgk m ʜ!Q^laawJ"5鿡!*uwS,f6(WiS)*P)T8hp`ZBK ]oUʴ4Rx Iз>R}mV:ֲڥ֠H;5.)mB4MNቬiY֨SykuO>ڠhM=+IENDB`socnetv-2.2/src/images/diameter.png000644 000765 000024 00000000323 13040701535 020355 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzIDATXY DL/JjƙGcϜz `:[d5F2'2gs9U3BH@DlԘ.|H~E`k)BBvEQڶIENDB`socnetv-2.2/src/images/diamond.png000644 000765 000024 00000000360 13040701535 020177 0ustar00dimitrisstaff000000 000000 PNG  IHDRVΎWbKGD pHYs+tIME 8 tEXtCommentCreated with GIMPWXIDAT8˽I @/^ڍ`[c| "g":i'@dya .rcS5lI-mt +R^Z7"| i .IENDB`socnetv-2.2/src/images/disconnect.png000644 000765 000024 00000001612 13040701535 020716 0ustar00dimitrisstaff000000 000000 PNG  IHDR ꂣAsBIT|d pHYs-5tEXtSoftwarewww.inkscape.org<IDATHQHQ3[bZ.+H0Ӣz饬I) E^zR$,H- ZBG&4K[wǽ8sU"C)eTUqdo~>릦 9ˤV.E`p"8n[q1;mKEzfuI $_p {^^j]v6٦?}eO:nlaǶ6>,- Ēj؈/<&y("(+fcd.voD 冈pz>0#" ss2 -!2EzZ`p._Z;ZI:1<Z 5\NKDƿ`VJ[>ȏ4eN4\p4!yiqd}!D`SF>Sij~%·R ,+@lk} 5tk8k pXH!Q 3E@*a|:WElJDPJJ9{77Xo/NG%"-i jz.]z/恡HUոU9SVF=0&֙8 ~ #Ct^|v;LLH"x /vafCgv|YS)jG;qĪU*s07 4y͑+oY6aSuq^{U=(!lRU?| دw"d[3Jg#Γg~!G;Ǐ?p\;zח.=Xz V|*Yxۖ- Ql`0`X6n|g  $"Hܤ ǎ5Q`0hZ'ͩN찞&"E<v/"CY,! X,A@ 0 HYE G/e$pQ@n 3MVwt\>#odq!-99ҋ ׮].OǕ+KK`CV9:PHDnX,\$._Vа]u~SX8m8i/?"hUժx:a2K/ĸ([ |BsxzPHl %icX| lI"g۹ADT`%s|NBՊ֚66j>~Vz <8BDd-/9rصӧ?I~^%[L3zQSVN ͳ˪z0~V9"JI!;ղd݇s \Wz5|>|^/>^/ӤW϶mE5y>j O%%i@x}SSW;@hq:I$3k n/޻\Q{>/o^DU9DDFe  Uceǚ5,+*"rUV! a'>v8"EL})?ee͆rQܬG+*b47oya0uuuFGŋz85@ݝ;鬩x(xs ʙ_1ADr̙m-#鱝2IENDB`socnetv-2.2/src/images/distances.png000644 000765 000024 00000000434 13040701535 020543 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzIDATXWC!&, ”2ĉ?%0ɅEz_n,ߟFb˓lZӤWJ;@%UlDПkxe#H(>=x* O9JrTןPW t#Y'Cm"l @ElWƄ=HFq b-y@bX*&=+0B@mIENDB`socnetv-2.2/src/images/dm.png000644 000765 000024 00000010320 13040701535 017161 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzsRGBbKGD pHYs  tIME  }:o"tEXtCommentCreated with GIMPW+IDATX  kZ?@QJ+PUm-KET}iit 44C644$TR|4444?>|%k?+CuJ+m- ʗ@|:IENDB`socnetv-2.2/src/images/download.png000644 000765 000024 00000004615 13040701535 020402 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzsRGBbKGD pHYs B(xtIME )! IDATxڽW pT}޽L6!!O0!`Hӂ/u:δ۱3vvhh-3 j"UV@$’B&n{UgBs瞳.--D snyl^m]uu/ڒV#ôFƦJgO~phh둃|/y\C MmʫZT͋pl`M03oڈf, iA=+Xxw_&>\ |wǢſZu=kִv.nB = %F^pAG-R dEbnKVo ?oXNEEOwm˟<˰Qp&QA&hy}cw/?=о!p=K~<̱9̂ cZ=a^k9I4q pe0& FI ׆H\'Wr ۆKWZ[vuղKΘXue;*7-.;0 k{RYciA9HY t|:'#+V|cYpȃa!Ä .l>DY1A<A@IЃXX^al~[KM!9\/Y%ES.CKk%&yXA0EFI#p!щ8L=F8G:uQH$[n2q 6w^2yF Hp؆, (-aZ>JidB iڐKS|0E V^ 5 7pV(^k @iL 1NH0M{"CyK Y@#S$dLnZBVT6LM@;UMOX,a)Ul2%npJz82F `5hTTFIOFͷz`MV^w#i|*,I)a\d*E8 =84TGt(`Kᖅ_h56c,}}}ٍћg],`X]e>IOd$#) |/S3!R Pqi! KnZۆ@Ne 7rYX'$]7\0ȩ$NeO0E=E*1C&OdSui#OQp3{!U9;–06l)}Q$Nc}, (*43QbSb&`=NKLYP+,,gsl(c- %d?"q\^n:[T;5 %J,ò gHEgH"$9<4z341g ['iˉLKm C  |-yOfX:ǏCILNz7,F!o@Wvesj A&U$ȌA/.rK4|dT6@ulXSh #PLiRH̼Mx57ܲʎEGy[\4:=%9ġ.`y!*`7hdƂ(yk+ҹA77?n&G";.3h˖8/ RY| ί_ZE g𥁩,[;eY 7K!:~ C|J1@5Ά!JOQIENDB`socnetv-2.2/src/images/eccentricity.png000644 000765 000024 00000000440 13040701535 021250 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzIDATXWK!.\x^覎?G%mJV 0o8l C>%+Aus> #?L 6i+'5-7$NiAАжn·#NfS;~^shk=?:S Ҽ ~ݹvvz' XiЀ I b1EcF}jr>tL0@'6NpGx[f]铗m{0ceG;N:vKhQ?QENKρ,08"uy w+0+2ĝ{]AO5ku;ҚIENDB`socnetv-2.2/src/images/edgeweight.png000644 000765 000024 00000001167 13040701535 020706 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzbKGD pHYs  tIME ;V$tEXtCommentCreated with GIMPWIDATXϋMa3 (?&l$eŠd`)b,,Hbac(ڏdA 5+%$c̱yn9=νgq<}| BCZME(E+>/`[tZ*"`F1?h"N`_̕m g"w\0po[X>W l]z{b𧘨#V2&q1- x 6]<-v)~&2gONGs'0U^6,Z>EeC N 8 C4g (um/yyY]n&o1r$v[~+c^rC9|If*#؊׶ +ñG$`_bъRyçMúd8qjəd2((v9d03BDFI_dFQIDATxc 5um@] $oc&@FfZZ:*̌ @lc]lf`P,=t+;'7/7';+H_HTJFNAIEEYQ^VZBDc:%IENDB`socnetv-2.2/src/images/ellipse.png000644 000765 000024 00000000740 13040701535 020223 0ustar00dimitrisstaff000000 000000 PNG  IHDRVΎWbKGD pHYs+tIME 82GtEXtCommentCreated with GIMPWHIDAT8?kPL &C6L3,"-tZkBBn%/  .3= )' (J%b(}VMM($$MY4VMcNŽ $xj1+k 3ab={dk6|>煢$F9W.K @c]#uj@, '|LQZ>&B$Fބ}aOfw8u1'+]g^opHǶSU6izLOPUTmy4G;!04߲`Yo7*-/!IENDB`socnetv-2.2/src/images/erdos.png000644 000765 000024 00000003440 13040701535 017702 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzsBIT|d pHYs<tEXtSoftwarewww.inkscape.org<IDATXWyPw@%b-axKǮu+2ng[tjoi;㺳C ԊNJ:c]Zd.r! "D#㾙L~~C O@D<$KMTOTȾv uu8R[{4 ,z6+qѓwН~B>{ ``BpgSH=~u\ 9v;|?~a:ɐfgc3&@$#""'zr9u1˃~֎O8qMM-Οǧn\_c{ @ ̌B0.ZSǿOx'sx`*lMI1ݨ(71g4GOCp^m/5 3_{$wQrʤ8Y O|l|2=0k)L284 t`2hIaR fL'.QV ~>CWUaC7鋘%IA䭘P\FDDtwmvF0bOnIHOG kAvJz𹧒]s_[lO 0\f~ojIѴ`i-^󸈲Q0wTK$ZJLxvZ;GWFDm-LDADy\0,3]3>WC"[?{H1lӹ24iY@S?3CBHnP:q{pI8uuo0kt "-hԸM Mǎ '}R1T*)`RR5^_ QZV]^Ve\L^b'@ Nf%&Ҝ |"06oF=x%3ЃDgjui_ ߶46Z>01sPJ>vDېG\A"_68 xVW`6bdt^Fq&t:ߋ ;r\1^]W1k[R3]e.'bU%3 ekyS3V(z"%'CӃN3bw^L>wʜ\'UAB|XLk>rgO4" 3ҋSm#@u>'?d2w ܴX :eE3¦'t/z.0;}xA>dg=5Uߡˮ46`a4vU`ܯG^ ??Y[d,SDibTU7aLWF48 ͽn)7uM]nZSzd/Lu#Gm6$RI*^9 jZ? u *y3)-Excn4< .{|鬍kynjaiGn /CCY"o_ŞQH` Z(&]~S==ڈ;!!;zoZXҠֆW W Zj_4]kcO=$Gڐ:& Y~I:|7].ط(6h`j}a7XlPCVJpOJH _@ # ,{ZNJ92UK;_Rb[_c# p0&@v8p]t}>ZN4T"۫@@^׬ 8 <&w_჆۾&@#N+_V^ek6b돜 |2 :A9SYlۮ}~C60kx " }X8޾kS7652T$ 'i$CaB)8a̍ 4\r8xE->7o P6[ 'jvgfbӃ0|&^σMJ| HF@#L;{?SO Gݠ0Rd暊|B AS '7(:Szd|^NeU7ԛi,\}Z|TL۟ PV]v8wHBWUr@EC|uu,WPnLULEeTBNJG=Ί^dHpt ߎ(StJ2(J2&hHFLZN3v/q sM]IENDB`socnetv-2.2/src/images/filter.png000644 000765 000024 00000004316 13040701535 020056 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzbKGD pHYs  ~tIME65w j&iTXtCommentCreated with GIMP on a Mac_[)IDATXýV[l۫w׷@JH$DFJ*mժjKy j+TVE}@jU$$zQ*( H&8$_ls,G:;ggg|gs'N{Ci""Wrjg`s5`)۶d8T_h+]m2;vﱦƏySOrRy빟B*%J4l"ئiiBRɈqV :=L&o{yںVDR)S +x[x!_(PXDTFVGZCр뺸q$ Itq0vdy hpW{BR!,J48؏ޞn׏nqfӥF]!|<B-@^GZET2h|?p)٩džv XE F[C3gf{n!eiuu֚lL")$VWWѨSRFP@nii<]^Xl.G HϿap~tf4fNL`9LOs#̤eY_y7@י=ngq`Cnݪ~LN?XSpf*nL? sgf~{*4) 9!8B'B@8[/ i:3s٫' Kt,>;/+npp(; @kk}pcM[O>åN{m8](,/] -C0ãi:}2s4ڑͽs{Oj5fYBp8x+ppj)%@ܬ}gP(=|jr4%  n4f2GȰSYP( q> 'Oy̩>2M}#G_]um\K5Ne^VG/\߶z$۶d5z{{?|]xvS ǰf~r˶0m[ gNLNhvio VE "xdGBѣ:[Lf491|hp`p]PJﰚ#/ͣ\Z@Z\.bV- 3<3 uQ|Zpvz ZjCc\% TR*0b` i> |Jj() 0"^F|~I:RD؍PJC*yE-J饄2OQ*T-c:Oh}t)XNcZ2~O>?_bt,>uj*B;Q|7}F>. Z   )H]z|bzO%Ii;v_P)(Ajr @.RX f, Y7vsS,c7oyEx7X.`mfWÃ/7kEq&T#:JWabPXȯ;(jxE^E.yblܻ';w]^bQ\*^_AhvXWA/hAc&Hd[yC- F(4 IPBmK[ m^9Ci |ɼo?,u2;ctjsHY$B:B7t$#hYYt]G)%J)#J[kׯv[\EJ0lMp=T6iqשtuHdGgv,q=\o4,@uN40<H~?3,4)ƛO{[;[(-}zp\pcDů̑) nUこwXt&;L,/LNE]j Q׽|r{kUb?8WVV_-ϫߎSq H|V&uZ9X';n3y/f}gzNؽ~R3gP҈d*3y͌g[z,ױzTZ[/d1/G̏RT̝}ZQ>)˨2{GVIENDB`socnetv-2.2/src/images/force.png000644 000765 000024 00000003417 13040701535 017670 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzbKGD pHYs  tIME4%StEXtCommentCreated with GIMPWwIDATXýkl\W޻{ڎk'I[HhV%UKT AˇFQT U<  uBJh@#E u3o:ٵ vqF0v5i?r Ejk&s! [" r. tm/>U.^k+c1PĮƱ@Y`͜ 0b^z5>o)0.( VG,6aU1|lH ;lUUT*T H/ ce(kae;R(=/ru=4Ew2@&_Un`KFhZ1  LiBJ5Z۸Ƣ72))G0d]M59rD,PsrGj*[khlt^Y2R)l.6FHO (83pe^vȊ7,7Y| .6)I L6i`p1ɄTDAK]4hUj!`@)q ԰$n}*ebqƃl]-N_,j45<*"jMIVڡj8|"ݒ@C1uFDbdJ14aH ;~>Ng/c|9kQCtNf D7V;G&EiqxDfR(.d!Q43?{1ǎ7XHl'`4&ocOE/J1W_Xꏳgn-Es]g- +Fzy_1ƘN uHlA{!gKġaRgrZgp7EJjS܂%Xwϊ7~{J < 'ч,6Hzw:02bA38nE8wyYq;Ç2t'ˍ17".Q6' 9a޽a```uh-ed_lr>Y\UHp?E[@׍wڬJU@)ᒯ93V?)@m6]T5=N`0u^?D>L{z+WrğQE)bN0&iU_2cZ3:2BP~=#倯۴΍?@K]"sQxϖ>t^SL\'JL&D"}v}ѿep 1jzy0W/$>N$ mmm}ܙsԑ#G^>QxWWWm?e?$eКcc ԟËe2Fֶ46O<ĉw=rߋ| "^ZY VطҖ{%-kT ӳ Ay}opbCr |Dֲ,. 14|4q%pu<6qiTjyn5m'oٲ_ߙ.R uB)$;RooqN]vz7m +߹S=q7re_5}}}ض}ŪԲ{Z767_S]B[n}{-MQz-ztJR:N /9H 6IENDB`socnetv-2.2/src/images/forward.png000644 000765 000024 00000003157 13040701535 020237 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzbKGD pHYs ,tIME 3rIDATxŗ]lWckfi;V! T""J}iX`ɭPU$ @H< JWN BC !%J)Im)_κ;;3nkĕZ͞sϹg9K8ã;@1 !E;RL!'3H|8F@ ۸f_n+":^LdM?*پ)U8\$<:^sjttMavkK+ ֬^═=ȧ! pw+%ǒ˛ǒyCqnNԔ#=ǁ7E`x~1{o_[S&|ӯҕ\-Y3߫L],)M.;)bMZcIR?i(hP |i`?r%Xzc-g^m-|-# mhrd'Wj$_*Ͼ<=Zx藋-ޢ3}+ˆ, uCXmYT `b$*ޏ|YcI0L-i*/X4t编q8n5lѕ ^͚΢iP]@U\gu_.9Wg8smk?H+59e 8x  /d_g[1[ʿ%{33ŗ(`6Gj}ůqz̀wZ <|[}"^R^ρ|2gžO`hI~UE!syf$Jr~ڶ-Zz6+? z|@oah*{]wٖ:TA5e t:h,˒LNfB5P_ҟR0蚲 /8Rk S̅%'4=<0 U۶gB۶,+v3.PٶQUtuHy^RQ<=JUejfSRMp:Wq]ӑ:W1RZapJi '<>k;.Dl<"ntFl#3at 6a\#">/bUD|aqzLcyڂzt1ͦZ68cY0=xK ށRmٰROwbãuZ[)3Q0&}KG>_vqIm^5R6L&#؏(S?'">)ˁlحi\)SSȯfu|élx^]Qԏu q/GIENDB`socnetv-2.2/src/images/help-hint.png000644 000765 000024 00000003151 13040701535 020455 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzsBIT|d pHYsvv}ՂtEXtSoftwarewww.inkscape.org<IDATxڵYhߙ3۝{-ɒRdY8vIN].]CmpB!}[i^66Bi0RB(KVe),EWWWm}eWZ9,c8 bv\cq!M _ \9ZiMwx5º8?ʬoa ǿMzBh2@JP LېFj=O0y1} {NPQ  vH!МHlsY?|+@C<+lLrS벩u8uߏzm+|'+O!h|1 ɪkZ1u&&ۼ/5s:a|yW)4u1.(@L,,KxTʫ} MN4'ykDm+{&+*@t FFl{Rz#Gp?eC6-G$LHa'%HZ9!1H,x`9Px(&nө3 iYuCҁ`톷g`jÔ% -8A7fHy5Jm>ޟ mEayDK?4~9K*1DN,?NP=p?8FQ[䙢T~;)S)s%>ԮnP1hr: J? 25T 16bҮEqQ5O.7D呋j[X޼ד[# BΜ$<''?~qv*F<_~O>P3+6fՉ59u@ ʖv3AE @$A=05Z&:RI`yyJAK"e8y 3cii Qs?}N|رcA)bZ#l6m… @w{yyܹK.wο47ߨV홙ߍD1OplIENDB`socnetv-2.2/src/images/help.png000644 000765 000024 00000004212 13040701535 017514 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzsRGB pHYs B(xtIME\_ bKGD IDATXõ{p?{oIHA" ã`> ʌP:VmV:h;B[mKu * %AT4l)Hk+0gh۽Reœ--TUUH$md\d K@?A$L?*0$  em:9Ժg*g~[#xˇ1Ӄ^6Dr0$O"@" G0Ji3$Sa_>{ ɩI0r%@Њ ^RĀQ \jHR&Y =uQ htI>dNJ纂үF)/YtD GI[b2:3o&&ġF{0TdbɝdΞ^R˯=iʧ}%G(ܷ_v `RALJhhLd _HYxc|-ݹCOi9}{3^Ǖ® ϒt)| @۲ZsZY\& ܣ3lCpnJ}I8B(ۘ{O<Έ> fE} լΩ؎kXZMea ~{QMr<~ ڌk-MNSYsw;|++"?QD+Bb~[+xdM KDi1^ Csq/y;DFZ +чǻ.{Gɖi | Gx)3@z i f 3-}>mmW EJϗ$FnmKTe3,zx۸Սou+V'@`A.CACIENDB`socnetv-2.2/src/images/hierarchical.png000644 000765 000024 00000000216 13040701535 021202 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzUIDATX; DAiBJxmQʦ6I|]ۻ33sNvCHR#}3].;!<3{i8JPr_p8d:V&>< < Lv@zz[>@o8w?N˻`x %LًiEYཅ^*ୂz/8{I6Z :ٵ=ı]oHNKɆ .aLrY4Ȼ6Q <#fUyT|s|thRb"N*M6s5ށɩ߶_Msb'}DRK2RBsh:%7#ޮh8WJvҿ@u!D4qo&& z 2҃;f lch> y'EݲDo zaԛLoivgJ6A NZ>tאOª67v@+Mª~ |ⵚתOE]\zI7|^}o|<_m +Mڏ{Ph2z҉rkPW  '40t4[s){>T/9_r@!N;p8 {v\<~2];g\1'08N8lN-R%_ w "-40}̥O lenV״cY>55%tG0/铖!7蠤7~a賶Vk/d0{ٱޚk~(lLB@jZ=<>5/Qbd0ON JїfSSSZ=āt1 yaz:񳄕,|dJVկԜ֭a-îωF0ݮ9/B6z:IENDB`socnetv-2.2/src/images/image.png000644 000765 000024 00000013004 13040701535 017645 0ustar00dimitrisstaff000000 000000 PNG  IHDR@@iqsRGBbKGD pHYs7]7]F]tIME 5MIDATxip\u{7h,H HpMl-QYƶ\rvĎ'q$Lͤ5N%=Į8'JedEeJ)&qbGht{{h|Jbު.6_{%K![ P5L4 @G_iUqL8b0e6B`nv@]s !ox7/n-#ƿ|5W^dI<fPeN`ˡ`QvJZB`Hua~|g3=|ț4k_ZS4rF yb 1x>X!#u `H &#|lpvhkbf^>7Re56"X2fe/c:ہ#qi~?,\:E3A!z{'*JĈa:)&O^潴k7\~i ;E@{7ә 1|CVI ]Goټ. \e8|q(M J4_oBX&;Ch9广0B"aH: po:C2ӂcrz>*j'<Ԩ5~7B]8d+,q)m= "Kb)̺;he8r1u'TSؾ5dn h$QhH2?^kM;6Ql;aUPͺɓY|`8 `404ml gM&oF9ms}Q|e؈ZWC͟ԡe߆(?WA&I}~f^>Kq"`)4pP3)S \ey''xs!@f*lMiƋު.`M]83oZ`lԔ0,RIST,٧H^$, b8FOeL?V&d5̑ƙ8p F XSxY&5q{#k?f;Lyoe[4S]3o4ѡ?#6%)9RxCU;-9 qpn7`/Lim'9ﬧh9|j_0-2uբ;r$g.1dWhYqlq,T3?&lH(T"Y-L c->K4BW0ĠU 0`_F^Qx7;[پ>̅o}W_9Kt(KrW3Ca_X+ XA0K'a'Lk2\p`H/ : W_ _$ '`oq4"ƛ)< |N%7#8SsS5nWqU6f9;љv רx@i)B1*;: S߽6ǯ^z;7ܷEm_\>}L] #i{p;/&?E0 q%ڂCŋ>~) (BT Kgվޮ&J 4u ?~Z&H0؍MЏpue}lﻏWҦz }Xh|¯I{谯qtxjgӧƐW/1t_k=Nn&8Z!'o{⅂DBpH5HWkO^#g| |Slj1t~f~njn [7q%D_S{a:(Ar 䃛9B3EcWK73A̴e:afO}R*n=@WGO031ق m@ D7n(tRV2%GB<K%D9Юd\&P izK'(5ue@†e}GuۑmLTibR71;:׏c45aP/`U*V] > qəESj)͵^Vq°fhmWْI*MXB,&}<6ڈU4?^-xt W̗7NDdm'n!n%K`6m<2H!hߏMrtMfϭby}P5R>($Se!e[(4 X{lk [2= !n~Q4t }plWp/"wL-i3~xyy2Vg#"Xۿ[B Ώ8|K>[ WB.xmgK]sc#ۣB8s(]wSSapnቁ**Pdbt4Cq²+-S.wR8*eQ)l6;s?\iKފk7Ac=s^->AXFR]qKv܋KA0 Ů.rz75\֋ž}o<~eB%NtyUc;Jvr,_;6F)oŭwDJ )5E[1&;>TǶm#dg 䦝b[ ;wp\uvSrzF ȰZlq@ V-JgS@ emຊ$j@m. |8 Nn>!RJ򎮵h(}5j0&4[Ub̗?WMVLʌ]c6JxDٍб|nl;( LOO>4}%[-|u_sp_տqyuw]wuw]w?4N?IENDB`socnetv-2.2/src/images/import.png000644 000765 000024 00000003162 13040701535 020101 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzbKGD pHYs  ~tIME+EI&iTXtCommentCreated with GIMP on a Mac_[IDATX͗\W?7Y&M4c iĨhPŠPH(@dQ4 &P?"Z(Pݠ6*AHZM6qlTv3~3;;] \fsn70YֹtV^Ž;_tifqxUuz*"D@DHE%ϪLD Dq\õfbFqPXm˨6"凷<]֚1 ȹf3\c@w0,yNT*|&W*uȧ\# fyh"<\D0!ƴZzV(Qv=2{s=y h ˀyߜ;O?7oBD2@2j+@{2 =$S._}yg˟+4Z-Jyb@T1| }%߽ZQ[ Y<480?DP,1z7&7.DkVd4lA,=xos[q0q6KIӘ4hٙgGeE;r5pphKoPQ6Ҕ4FDa?x3_'7ysF60rG!8\|r;ӼpwXqpSUGc]+5nU=Y՚%f9% cc|裻(8Rpظa}g G͂Hu ł_&zko{K333K'Դqrd`C;nxh\|)s^?x@rC~Xk24r,˸vZr :êJmQTۼW.\޽{wQP+3iF^g訞?={ =0+y,Mc={⪿3]V(JǏ?KAmye˖ "R|(w[_=5^cIENDB`socnetv-2.2/src/images/invertmatrix.png000644 000765 000024 00000001206 13040701535 021320 0ustar00dimitrisstaff000000 000000 PNG  IHDR22?bKGD pHYs  tIME ?IDATh혿KA?RF1Dc'J$@Z8D$)L#tQc?` Q rX1c37:;7 󅅅y L]*5# TH%H;pO%LTX`C H(=3@(`DL 䫥5ۈ6A2A|#nȼ6/RX&DaW͂`3 7!!mJPgMP. !>ϑţDde!~5Ҵj|ϮA 'I}E_ !+Fy u v20Z\Ln 䒬zM]1' Tŏ1b 1;b uMt6Fj C=b/ qi5L,)&ȘYA/q3D:҇%(((((X+IENDB`socnetv-2.2/src/images/laplacian.png000644 000765 000024 00000000513 13040701535 020510 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzbKGD pHYs  tIME  6+vIDATXֱJCAESlMK[m)`S Vʵ"\B90svw%ZsO&hZ, !fXZ0v𮱆U\bUU0Q=Ek@{<1_.Ҧ5#^$gk7pw1,rRE)eIENDB`socnetv-2.2/src/images/letters.png000644 000765 000024 00000001670 13040701535 020253 0ustar00dimitrisstaff000000 000000 PNG  IHDRĴl;sBIT|dtEXtSoftwarewww.inkscape.org<JIDAT8Mhe~tdMWSiC^4R(XzH"$o zSO([!f76ݙdg `xwhh4H$b8uIXŚ]bPJ;???82}/"Q뺔eYzbYV7xRsv8rC=C} uR*P|+\`UƠ'@ xT05E$TuκXDGvuE;"PwT{-P`ै:U[PhBD<3(sb-bH} <75p.C^[Zp75јqNaeEDdGu;8`2%[K&)BNSN/wR8xd+h)GG 8L*b ;dWűChyn093CJNsHQU-k U%d"^801tU_vokIc>ϟTOިb1_zVuIENDB`socnetv-2.2/src/images/net.png000644 000765 000024 00000002436 13040701535 017360 0ustar00dimitrisstaff000000 000000 PNG  IHDR ꂣAsBIT|d pHYsֲ4tEXtSoftwarewww.inkscape.org<IDATHŖ]Pe|  )@yQ~:]_!!MdT3 cZMy3I4cMYڠ3Nڎ ˢ.Xe̹߭x?9W`nd,DKKMADDnM̦;{i]qֲg}GT͎*^T+@2`0\6`#Vk\4A,$D?ol[mytӾQJ5+n[vu>t?t_~/77e XyD,^xlvEI ^ ԀY?}-"~CRj` e2,_t5C)ޏ[V_L%%hh7C8A~GV32-fMNNj x?SjbH+@उg +0"R=oo"*DdXlPW_M.VU} d_FA)2yBDzN6wUv‚ EnۺhiF$zmajj&[m6>yi<] [6n.=|gK!hIw6`SlloH{ =IF/z>H)""CD6<5',$..k)#+8QJ@XRP4/wewjoUQQe: -TRy_rd>LNV&O>mmǎ9@]6ק.5ǏsGh=tKR&OLlƓ^fstهkoW=GZsTTǬCxHssҢw*hl#_seVBELNe tz?z+?E渳3p11z FD*1^O.Wg'fo6p< ee4-~0//}dY`:pxFwK2o5sׯćiqƊ v(PJ5{~>-a;J2ro,܇䦋]zZDx~N8_IENDB`socnetv-2.2/src/images/net1.png000644 000765 000024 00000002613 13040701535 017436 0ustar00dimitrisstaff000000 000000 PNG  IHDR M )sBIT|d pHYspUtEXtSoftwarewww.inkscape.org<IDATHHw_;kQjuZe-4q6b60j`e4nLز ƪ,E,j-+eR҄RcSv:>y<{NH)ZTFGLtg'͵ZRzl"J⴦&zVbgM&RN匛dxgBFg6=B?}@7zUm&!hƴ49"~OJ Q2PΞ%#` QSCv;Jb"ǁE@ٻoHAT[q88vjvӧylܸAGv6xGJIXUUt; h8uFq8psT~)Q8_Cl,Y ݷXAUUt8n | UE11wOǛ_} 4CEXxT[̯ә(ׯӹlAtJJ [ >{Foc#ݹ]p"!Ĵ@>MIa^` ﷴP[ױ:f%]nkE'o2֧r2>kE33-^: tupd"5 3#"0?x?ήpVz:E^BǶH6=ʲ6*EI}?ݭp P ! V6U^MC08a#sQjGJ^ dl#q\c b )B~]K#Uu4 Lx<@#1ח99\￧9=/8ٳYsٰfw*]R S9bfRX0^rCZ~\Nͷok'NKKis1q4/[9mO3$&rݢEǎLp8,_&!D>Av#HQv20&RJvX+R$$d"(6,\d<9/v}D~ xb!02 ŋY |B懒"#"q<{22ŒPVo6̛{55ݼwRad"J1RWp0d'Zn)@dLpB44pytGNףlߏu:FN$~I_ =]fMHUWS(M_AhIENDB`socnetv-2.2/src/images/net2.png000644 000765 000024 00000001025 13040701535 017433 0ustar00dimitrisstaff000000 000000 PNG  IHDRĴl;bKGD pHYs  tIME ,IDAT8OOa\ƘhqeՌt3j6V RB"1bYW(!t-g~y>1f2b]K`f򩣆8XEh6J|Gl+6K JXuVY`8,|6)lAww ׊],θؙ5q.lxW -- QqhVS]v{)p 19EX-|X#u^^pPYXa>g~L~C*d^Ҫⲏc-UqZzSX:0+mwb FqO(u[?W(,ȸEX3,|c cNݰwĠZƓxYTU'IENDB`socnetv-2.2/src/images/net3.png000644 000765 000024 00000003213 13040701535 017435 0ustar00dimitrisstaff000000 000000 PNG  IHDR S4sBIT|d pHYsrntEXtSoftwarewww.inkscape.org<IDATHL?{^~^@A)bEi éE֦Zu&iԸe]ܖئYЭY6t?RgIZc"prgbjw}===UV{;/.2*))Q1u/7-vbi!U7W.\@k\ߴaɎuZ-Be5ΚҢTU()p_A,U2~/aj]85#)F"&Z^14◨H hkgpWܲ;āgQ1?ªJW$C*1y ˬ6aSD= Opq~+=+6{ǯduw^x&瞕wEWML]ml~V5>9GXI3f~*Vɐ~_D"/"ZpMqǰ0[f=!-=w}:5P/l^bTt\1@mjێ)Fާ큆g/^xDD8cc'`U:_l)b@q.ec>LŖǞ:l`m 1??+" D$'۟۶ |zzseK?Wp,8XV  IKA\4TS N[$C*36+UDqIdK$K&aIR% v%Ʒ-y)+rWd;F۞$N6=35rH/yڪ ! INigڠ-:1iC09F44Kvt1|/Մfywq 1 r\wN5^T &R{sS3݁oD>8Y705?IENDB`socnetv-2.2/src/images/networkfile.png000644 000765 000024 00000002372 13040701535 021122 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzbKGD pHYs  ~tIME* o&iTXtCommentCreated with GIMP on a Mac_[UIDATXík\UǿwiI'4Ei&Ј]( MZ#"d?bp!¥AG H`B0j&4ZLSi&L~̽3;=?y0J7"2l mmm/ f!:lDǎ~VJ=N;Xd2i-..: sXkllvCCe9ʓφH H  r2,!.\t993>MWSͦ<\_Ar"ȋxg%466Y]]Uނa?uf2֛C W?Q=ӓ`6ǡsxAҌG7y! @A8zc0X{5?$P}{0*)%H@J(O{IHL"~ j`[ |>ءđOBo"(f~;i<8P[[d2caaKKK" zR'Wדxxy@- `ra /'7C}}}ƆYP}ͅO=gAI!3vkMǟDmX J~Vzom)sTˏ]'N:RTeYbB `ŋ.~DaXU6%߆ J) RJ@Dy1lƵk*@___i!ؙ@b9#D5+@Jض]sb@k^o)ǘ9w1/_hC`h4Z "bfR S@)%wQ\ pmDbfH) )esFFFB6\R ~8;Q!MXV: pF ADKEɓER˲Na۶5=={Ie(@6 r\  >_UnbbDA5Z&.z'_ ~Q0c%|pkxj8T*1:XIENDB`socnetv-2.2/src/images/new.png000644 000765 000024 00000001524 13040701535 017360 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzgAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDATX՗Na5{q\\0&bXX ѝRHECey{QJ!Iޜ3MN"SsՐn==d 12<S(CfIkR׾TUNK޷’=Oޣqnl4kZS YoK|mu5e<7ɺx&oFh?B~Ql% wIENDB`socnetv-2.2/src/images/nextrelation.png000644 000765 000024 00000001452 13040701535 021303 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzIDATxNXJ6L< TiNpqc)*"cf}$%LwٺOx7UiOȌa$H.W5)N:tϗ(K39wV+pzzFIL]\. 5TH..PXU(yD"A> F5QC0m^_7I&bbIIF dYfM\D:݄T";( 7 ؑTi8U͉D&yzzB|_/c vqA .z3L݆a]ւ %UA(16^^^Pnl|qf9L>_U77yCau^__"BJ`xs}Plt,l6Kָ 1bi:hX0ڌHRĈe+xMpryUٜ1n 츈vEZ4Y,.bGы`8#xD'nw6|LdkKX\xecYdv5B8䣸^@TUL=-M;;{7#ZNAI|* ͦL!8"2' ~? [ÝooG~xx25B!1!_]<D5@fQeRtIENDB`socnetv-2.2/src/images/node.png000644 000765 000024 00000002333 13040701535 017513 0ustar00dimitrisstaff000000 000000 PNG  IHDR ֛^?sBIT|d pHYsjtEXtSoftwarewww.inkscape.org<XIDATHVmLU~K0"_([:+[Sp n16dtLPg0iܦnE?\ ~,bsc4:ֵ/ '9?ss/#"J%p) ƀ4P]RKKR{HN׬h *wBPr~ĨըM`WD HIA$|{E!Fm\+@prY~9))rzXF*=eyTթqv{2"`4T{n` L]P LD.{Zte%:\p|y9D,ѲXDB+4qT2+ Q/^&ULc+KR?#"[@# @bg_afG=k|xZXXHlY7lomnY= 7x= KMEudRlNLc.8Oܜշ?lW#"b XU|cmg~8PSikS*IENDB`socnetv-2.2/src/images/nodecolor.png000644 000765 000024 00000004030 13040701535 020546 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzsBIT|d pHYsiZBtEXtSoftwarewww.inkscape.org<IDATXWkP~wY]...( *A(oxS;qV֙j3mmEM$ډI'&֚Ԫ4QT  r~e/}?F g9y=̘̈H`"fG웙$B/usIP DDFB2NC[S >nDd\sk3Rh2>?TT6}) s"#u1}Wb{պP'_[[L8v3,j]ϕ +?} &.QUX|ԾZ!O HH#҈ȇ"k3Lپ|HӲ?z{H1#`x p؀2zD_Eq>A#(/?64ǂL"|):彄ڪRuɳ ;v͹˭A*-w H`J*q {*1Ձ8}9-΁w ^a᳈r PV/lgf'0V%Ӓgsz=VvhMC{Gg%X(;jXY+jhltbOn8p}GG*= "cI-ӦaJ0Mp0s`4۸1%7/o8r>=/`Viup"?c5(q@Q!+ ( i^BwBkeRScV)F#bf~dV݂>Ž;H$ɲ(( ( M]pA0PlH &AK&f~5>~?PUnԩtUP\ݺo_/uTшdYV+럐&}wX:) C"ЇhK7[%} 7bRV1r35*{br9-cʨ Gb7K’4&kx E x c:@.,4ҧeF0alЈbNuBeeݻ Jև oė]D^U,!&7;RiK0RzKp+8p'1$=uϽUaVEG$C|笼`" ia=f\/&e3<Dx̶ᔵw_:_Z+2Ѫ3kpAqC#w,(#'$d^ˠ7AU05)rk.h_tok_?r nܩukf,$BV$aj\iwblGXRXU((S6ё%r]u{Qs]fB"J0 7wY8?5l=ŅU ["5VbUvƙxpsZ҂ 돼Цgzf~ޟcJw TIT=ζQg\~-&^,&Rレ(FW`1C1cCos:^gf0sE>.<ҷ9{' |`X՝c%u 3OGvTɖzAnt˛)Y(Vlm= U M=;cCIENDB`socnetv-2.2/src/images/nodein.png000644 000765 000024 00000002732 13040701535 020045 0ustar00dimitrisstaff000000 000000 PNG  IHDR VόsBIT|d pHYsP)tEXtSoftwarewww.inkscape.org<WIDATHW{LW TD#k-P`T.꘢Y3bfn3fN:c\ M|cP/R @J[(gbZysw1=s,*pO'VL-)~F f'e{ןiЪ;2L +0Ѵa4`8#$x{&%u@80"Xw9Rq?^.! |0Fi+orlx)F\eC3Y@D.\R空LSS{ fk<,`ϫc4OuW.7`*Ŧ6֞՘*CҀd<7MLmým2$"YxLϩϕEF`4?$7<>.Xj͚91&J"Ѽ%¥sFxz:'1h^o| uukn13j\@@5c9mRvUnq@MӏT0?i{ZS~SD @Hf>=< D f^AW&O4/WH^wJ[1mA!̌Y};.6#ÇQ/$Ml65ab)hCz5ek3 ]#:&t Jn(AHnt|}}~@DkCE"MDLDGQXdhw%@B>lmVeh.hJ@`13cfdTИ\FEadH#w'n Meboe[N" P @*3 3[j|*#T*(ˑ^Yr"}8 6@xWh0xO6Uw0Tcgf&"ɭ[HbQV"JDDN 毒%?%<V@hi,oSu7XSMDo̅DD2ؾI"7fFAa͎u"Rrr䙦XҲPtu?p5q&'YoHEnyN f5-VF\f甞ೊxh?ƽf#(*g2A7"sﺆqGJi-_H,1nEb㝍>uKWW'gH/OQݒ~k3ゃ yUGG+R䳡Bkn7l.\`K~X0&wQؘ FΠ%>ϚϦ^ /z]{Hi-aӽJpՊS\+yV|ܬK{KjIENDB`socnetv-2.2/src/images/nodelabel.png000644 000765 000024 00000001154 13040701535 020513 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzbKGD pHYs  tIME 5tEXtCommentCreated with GIMPWIDATXϋQBJJF)jl,4ЕRbP~ldc 4 %QĊbb&kLr}mέ}oÝ~y=ysRB ks(h`}2v7 T-0S؀:1>^wWUxθ滱=M9 nel]@->罸84kh%{>%82L]x" Ydt“6܉]| nxGsؒZh{L+=J1Ùeh^$VcMY| ] ijcqu5PǺv>3rwOvkex+95/ZY%0:jv3exI*4cn_PK=t>z[ /zS1>ÉT*TѩQbIENDB`socnetv-2.2/src/images/nodelabelcolor.png000644 000765 000024 00000001242 13040701535 021550 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzbKGD pHYs  tIME  K tEXtCommentCreated with GIMPW IDATX͋Q3yiHVA!oِ(Ȭ4;Ea,FrŠ+-M{8R6!!gaF.bܬ}+:bgY.e1IZٍ8R OT)rmkcrA&]#2ތCC%wVfj1 ] Mñ=ʡ;ٗdo6~d W|ckNA+JJs/'ds1V v@Aх( ъ*Z 6>\7_`8{iodr9;3s|@U/?0 UX3aĝn`R@t ,p 6 W!81[~NJ3.%=|yG)sX6exvM prWJ@7eۀC+K  x1 ` V49FF?Bьʰތsz6JD+yp<q!LSe0ͬo Rt΄S cȁ,0Oznh_ln[Pm+\b勢GP|B'D`ay CI \V nnwSh58^>*j `Ɗ8\ &%3}3fOmT߼BrcOݬ8*q$9.#`/1|Qc)9d%4:5n4i^HGaq@RA/y+uou$4)'"6۵֯;ߠav:p 滵33V"/Ekt:U` :}2֡.-sU 2,[IENDB`socnetv-2.2/src/images/nodenumber.png000644 000765 000024 00000001123 13040701535 020720 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzbKGD pHYs  tIME 57tEXtCommentCreated with GIMPWIDATX=kQg`"Qa!bE+!)4`ڈ??,X~bR(XP,-$&4o2fgc|{s ]l1j<^Or܌r} `3/U)x)yU\@ p4i s!R6Ʊ;TNg4b}ʐXwT]VK(quG(xױ7h.~-jڬQH?vVe 4pu36@:x3X Fq%GQ)0E0f˨Lq#S|wv!njrBޓSyo'& tEXtCommentCreated with GIMPWcIDATXKhQ;%bdB .DZAmAq"JWcY+ w QT\3>C#Z5&{\d1N̤vE\?\?65i0b$n@ S@gX`[cy1&@I(0^\,`@C_SuHjlP 0?#faɰVA) X`g -1\,FC|'x$ _6SWW 끴{d&:8#LeڷZ.*v=i~="˴W7D*k5zcA):@6(D7R'\?jA˴Z}.I3a6SY-Mpc4MśQD/K]F^JAZ`0q~ R0FcIk @)$j,~X,z"w]H}2J>'7|>u_W`@Rk"0/{׺7M3%.%IENDB`socnetv-2.2/src/images/nodenumbersize.png000644 000765 000024 00000001123 13040701535 021613 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzbKGD pHYs  tIME  !~tEXtCommentCreated with GIMPWIDATX=kQg`"Qa!bE+!)4`ڈ??,X~bR(XP,-$&4o2fgc|{s ]l1j<^Or܌r} `3/U)x)yU\@ p4i s!R6Ʊ;TNg4b}ʐXwT]VK(quG(xױ7h.~-jڬQH?vVe 4pu36@:x3X Fq%GQ)0E0f˨Lq#S|wv!njrBޓSyo'&P÷ .ʂ ʐ,ttoskoW3gyi&87L36/ӄκ6n'qB!Ϝ4]_Lo/ꮞA.^*.[W8"4>p='H$X$:D>& qZ^f,|H`PFe[y.Jh'[+࢙Dҏ+cO5x{M|TU_P~RRX~nPeooos4tfia!ZbrR٪s̮rW4PoC>Ѐ"SL"@DR)N>} _Ϡ /3c|;}}Ͼ͛!Hc3 Zngg|43Gfnx;W|%I'')((~f$ lY6MwGZUsC*薪pzv&x]E ,geQBk+ ]{WC>9}mԎTT=[764WXW2( %gmڐL#0h B|78}V(Uk BaclXf,(-_ ":-kK3yfn{5Rf=_/+Ҍ H|ټ_k_ZVuYV,VRp~yvS^ G[kwd닁 cJeO:䫸h@|o@"Wbbb67gg7] }- %XYYE#n+7!bԿum==]jc'F(QAڶj(c/g[X+._νVBQE 9 ^IENDB`socnetv-2.2/src/images/nodes.png000644 000765 000024 00000002052 13040701535 017674 0ustar00dimitrisstaff000000 000000 PNG  IHDR @sBIT|d pHYs"":tEXtSoftwarewww.inkscape.org<IDATHALw??(mjY'6NH MD<]Ne  Kh] ;xY!4m,&hh-bk)è@m/~>~/+AϔRN'X,XS)#|뙈hfͺ:XH$Hkhc~~ebh3"2UJ)#wB<~+3fL& Q³{$TyoΟ" M륷 x;rmkkLx 9--kA3'rqa+L~wq0}"x3jr݋{7ޗ`C}xNhvVÜ6GkjXؕi]cn*$y8LTfi/ߗ]v uH W*e`[ '3">vH~Y2ϭB9DQ臆={XSR,::NI1>I,`]YSMM4 1y`Ż?ū)LOϮ׼tCO3@{=.4W S2^5C`\&K|H$8As''H׻ "U77N,V>V:q,k,'6?F}oY1~ Z{?0L(CF&e-,!4{Z;~m$~CF=5iu?PZlY]/sq 1% cbjC'ݼ-Ӱ;3&SXO!,CPF BBsZ*tȼE lϛ'Ào%Q(/bM$"R-BySt < ] /yƪԳs LŔbK˫#7G<<b~/=/-:C(QNܿ}3i 3Th58 X\3>GelAC'b ER$nPD΋J~ m['Ѡe")(Ȑx5 >c f]KcNzI1N[;|%jJ0("3 t.| J*qQF@7 jH7'@L{RE qAAͷ]lC(N#KQWHoXQG `4QɫgA@?noiқ*/ru)S)+vQŕky8n3cjs@"mDžPt +4X$ |J8aVU;:!z/]&o8=cn*F׃H:381 ^QO`+] 9g=JUmCeS@%`+njǩD9|6ZZ8/95j'zLIdnFڍv9,9=NrG ;Fc9=pOJnX,P(@!F.[! o<<5iEX?7Ǎ: !p5U!4[7*4nI:rsm$Jesp)>|c.2*\"]~M 6tvw䈶"I m2JxLKIõ.|me6Y\Q{ %&6ǝՂ^NEXx]\UPuTA/ rjQCO-H/5z-Wxܯ\u˯Ev2g`§ҩ;P 9 /"mak^2/f_=IENDB`socnetv-2.2/src/images/pdf.png000644 000765 000024 00000010066 13040701535 017341 0ustar00dimitrisstaff000000 000000 PNG  IHDR@@iqIDATxZi\U5Kb\Q .qFg ˸(qA(.?D L̒ŘI/K׾/UU]]7\F['tFV~{MzvvLf92faϟ?!%2Ka_E88I|>&&z#|\QeXA2<4dCJR*\Jm^+)˒d$K"3/?4'3O9˾ pEKCt::*v{+dHo(IIRF3a~pYI&s0n> _,QYohBki˛oJYF'& Tk5-&;ψ@tZ@8rddM!xdFJ-Tucڽ[*V4<Y#E\+R,y@טs;,!*+p]pm)HޱpmlLzA2!BnI-X^V{6Lk 9P14}* oTaۂ!Kɻ`NH5`q`*uԂEIl x@v=4iEy9su`yv'@Ū.Hޱ+#3%5ԄCNsi[B6wpX7, <6sꊚ"Xm4m|b: $pXV) (b s@!%BP-Wh4%X*P0dAR_~X{2H!ȯC* D(4< NuX]"*Sڜ$6J*3ц%CKc| #LBPkI bTɳ~Ͼ UQ%𙕾geԄ F0 $I? L1A18#yi_(|+zE#BK^ÖDZXBLUNUWICY ea H!%Ҙ</!]㽞~L LŤ"@2qM.0pC""sR{3l@< MK &I{X>]2w;:_vIaLA$+bsJ>0`D'I,Sr@3gJ'Iܭ (aʜ:,D13G2"hT~؁!:Dk0|5|mͿr駒I(DR 7E!BG7E@+"e9otlCz}C#Eb D_{oqPuG:c.QĂ<㞹?s9c BF cB  {?\b;FnGNwkžr?ԑ^NY{Vo*_HhzjF.I`1ltL@h(5W 7(It]g\CYNIa53D \qKr@9̑Eq]P#^j 2?/'d@'W pXWx"hi"xL̚%/JsWwtI):|"!L@I gJ$Bw$YfhŽ*oP"L@xHR*19I'OGĩ2&hI<fr|K:IDrVK*NpR 5Ȣ\}cbKJ5!ęFcyVIFDq3 #VW|E8H$,+hDQŋxo( !% y#t%jiag5רί>?t=cpKs0D EQ-_->)q{P["f)E]0~xꫯ${pF0,j$WJQVX!J'fMMMF+W~3|{.dB>W͒6IM"eYZlרxdY듞ƵIx׮]*;vZ m;0d|@[bRG "ǃ0Ψ\g<`Qh!'y2$Ϩ3$-7oM6 ukrٍ8*¤!ϖ&=#XdsNFqC^(yFwk5-[/2r$)^ ,R]|WŐD&Lc^ƫ9yt,1F(S) = sǏziX(r4P='>Bi S(ڟCF<?D5~CF W2`ݣzMAa5{yt4]x='Ija1ۡ7fQyxwǫ@h?O冚2T6a&1F 6@uѢ_ca!C!8E~W[[))Д 2!nQNEčl u^%)%5}rqC@"m--2(-xS*|NGeT}sSXb&NB?^0 ?3Ҳ {I:ѯ? q3\y:\{n?UѝPKv-朧;J [&<'?~(ˉ+V,G?J 6Q)AEiRAbrOsù;ng9|diaG?hY ?BLh&Ӟ> BWD!cwG# ^eހI8 o2<y@Vd+N4pN{)7\{8x\zRx3w@>5ǎ YKn; LQ/i?-PY@gVş0 <`g¿ ٫R O'+PA$ XKr.> IiY$\>F96 ]ȝy#=/Zr:|U(-`=x(pa <,(x8̾Շu^?8\ls5\?<̀in;6ǀ(N߃k{:.cYedKK{{z>m+t9!P(oT  z'k+@GNB {E7.%5iZ*v5´R*K`=AݙHS/S1Y70CNT8'(+&' B-&G,A'y1B|_"ktY,S _IaTL68$ᘲl5<W\dpN ףd6:vX V}N*FQ?a'v>a}4i8Burgh*owi+aNx2Dr-+]Sάz4BȉN؃&K1.i4(FFH;x?B2jոT d93^t9>8@(1T`WR "$ +=Ǩ1R +]Z#0&BHGN`o\u?xW}a /юcϵ7V YNp,fZV}!b&Jcvv2wgs؅?3;w79,Dw OpfM=p@X3cP'A:-X:u׃z/dKq;bC#[ZJ?=y~ l0OӟxhDt_n}`9_r+S';!ޙXW\.m^=z2|Օ20lތn2F#cO!#qeyyb\<-+ v D&GH[6hq3uA ̑<,hvp}̎?paZQ@->Odö~k=̖3nTtѷ$DV *  h^#/ߜ;r߫ e]58Fcp \5?u쁵 2ݐVJQlܮN0};DUm_wD ܏jX/IpAS7!R <EOd?΁pͨVp,eaTZ>hWf]P0hkƴf\HQkM:62) FIj!.? 0%ʟIENDB`socnetv-2.2/src/images/prestige.png000644 000765 000024 00000001715 13040701535 020413 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzbKGD.l pHYs  tIME &-4I&iTXtCommentCreated with GIMP on a Mac_[(IDATXŗMHTQY4w>6@c~Hd3f A]-2(hQ0ТE;Q,L (kef͝zμ<{s{d-"GW"^/tj23: sj<+x8Kf\kH?Ho\@/`:Ju$*=*-c2iV- [},Bi $0eD\uBp[v_.6%ՙz3׮TfMFZwU"$lzG>56Uy9`bSȂnI&nIԪ F@7  ]le0VX8(Roȣ`*rd F* l3M!ߠr`PefQ]S>Ʈ4,͚ƒ7.o fS>jԇ bi d΁C=/1hl A6y-fY sA`Kk?6$$@6sYK<LԉW7(ʼns,e8% ei +a~]+{naL͙u1%PXG{XÙx29Ȝ G9%@]H <~)ԻåπI@^l})\ L2 x:Dxx׉w/V,hAm1iˤel5 >wut}CQVl*^MN6?\mq~(KG{r¨8qЉ~c:`0;IENDB`socnetv-2.2/src/images/prevrelation.png000644 000765 000024 00000001502 13040701535 021275 0ustar00dimitrisstaff000000 000000 PNG  IHDR szz IDATx1 af࢞k6@`ژN<`r",L/ladZRuc@=s}B R\q2+T7^陝s}my{BD Ҭ9ey *1mNP{hdx=pAPh|Si kYL!q2\'v=^6=TNO=q{Ft|vva^*k)NW.>& >==Cǭ1̘xrcJnJ]wH-6qVt]F۶'jiP.*mAx{ A -ILuAI4ƻ; C!QlXѴ{3\x%õ BBt݀"olGN&1/m{HZry;"Nـ& ňsT0OvJ%]KVauL³m'!t= FcY2}f 8@XvvT˺^\  @*tV%!*0a4*4V3aђ 8BcZO}_i0PGIENDB`socnetv-2.2/src/images/print.png000644 000765 000024 00000003304 13040701535 017721 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzgAMAܲ{IDATXíWiLTWv!b%R EIVMlihŲF,aQH$d"BYD@@}S6 $ b _Ϲ1/޼{s=yT.;/##.Hӫ%.VGFެMLj/6A+WJۍ|IiZhoGsL ӈ=뗉P 2ǑmXyc8dq[+F:`~!,шKKB%3 -IJ9"g(jGͨ(-1' N t;Tcu`gĎ:6 Hiꀄj lJhjꀯ+25D. pR1qPzahhx! PPPxxxLnڴivmcc+,,Lr\|իWFGG?IݍJ68p`u9r$O&Щ`C&}666s 0ykk(TY{Um VOU;aB*y`---ɓ'x1=z$Z gSىDsqJRwL ;`&H0n޼) [n߿ P8K?jΕK WLvv(d???899رci!&&HIIUEp01ϒCbpubqyxz 233 Bt]QdHOOXzLM yu21 4%+Wؼy+a7oj);c!"bsDy㜫 {KxMOO 5ǏG̹*9]pT[\3gЎi8;wJXwMEɹ~P߷hM }D4C idP?xaKs$N:jd}...s myS1DFНq}\WWc˖-rvE `mm=+97-᫩ 'O%|h6_|͚5fff#‰Rw=eس dQ|ޓ˗&55K֒TY~Ajžu֕ڵkw@vx- ŋմJ[vrI4Eoŕ*"u%<@[[qmVVV-۶m=ZYGG|aBOOOU@N(Ή+Y+h ,(D;ʕ+V^@Yp7" 5򵤘 ",Sre~TJGcIENDB`socnetv-2.2/src/images/properties.png000644 000765 000024 00000002011 13040701535 020753 0ustar00dimitrisstaff000000 000000 PNG  IHDRĴl;bKGD pHYs ,tIME :IDAT8Օ]h\E?&$I4Zc$ҨXD /' O+"(Qg-QlJMmv&ݻwƇI_v{`~mҏ+epnhlS@.ŹBF\Ƭ2Kv]d@G֎m,BIڪC6"E};j^~| Pw]{x0{O$Tq]yw^:UGZn&YUk[/RݢXNO-drqMJv{Qr43ޘk]֟T%tVf@ -nǪڊQ =U"iw*l7ƥ" -6! V5#`"i{y`Wlu>UրK 29s dFcǶHT-lѝK,hUNnn64I| 1:z{_t7ӓWãH+?OMT}oiF"Jt ?~咙HmjgW^kG\Zvz/K#M|nxz7QND褍Qőצӳa].Y=}oea;,0 8A%=ZpK?t~}ӻʾy].( ө_}r~BcUNm 3^3+ >DsE}|ͶpV[l`6UOho4lt,bn`˿q*m.yK?=v>+IENDB`socnetv-2.2/src/images/qt.png000644 000765 000024 00000004716 13040701535 017221 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzgAMAOX2tEXtSoftwareAdobe ImageReadyqe< `IDATXõiU]>N;)-LK@U!mQjbEEK>Ƹ%+$@R(`لvZzgs{z%-6Orr·=yyaAl[usfGuBL cLð[;boXyoup`JzvoD1c@ (cb\p=˻;EtT=ci}a6{ [fաʼn0=.!$0m11/;P*gw4hקx`D~Ջ0:BbRycLU d:f;:  H!l/o" wՆ{q^|6[>ƩQ5;e^PsB1SH@k0Kt#0DU]-ibI:m wy/C%o/^8Sr?9`Dhc0B`0QĈTAAܒ,JailJiIbem{~4 Xu;v$k}H a$JPTb1*Q^@drqݮ.:]"26D2L@"P!(qۢ7.OwO?-Ơ'{[ZlXRX2?g(~kbu׸wM:jP<ҺMW_j뺯+O !n  $3 "?T o;CR^J=d%6pKx-GY=曫b2&td> jOBKڸZǙWW(WB\c:"VIuˇ+3ĝ|O[ 0$;[~e{査I+@)$^T Ba"J_ |IlE+s{'馊8ubJ2>tUk.ŠBM- 6p٦;6d,"}2̣Ef|>p4GF#p {df6 >f8ٝWm,;RGd4FGH!{FPw\!(s~ɚ6qq>IRˎR+$tWk6o}rbxbw f|"!BVb5)d2{5 b;^c˵6_?7Tuޒ(.xnF'N\_(] \ ԖB쎈MA4U]1IT u(XR!wms\#ddY&fLd>v3KΦ&iK M>ϯ#۳u8՜!M9~JjsSu7l-*MR }2 "᾽EOx CWxgyKNg$N6083-+zb$s㌜xn \W3dt.cJexK#@yoDiM-K&v+)[ynqB%PNSb^fV+}E,n0\O Fˆb'սe^A:LT[F^"`xJV4?ZyZ D{q+9w}̫F8ۣM2tIǭռcL%G{:f+hZCȏisxϟrj(6[wyY%,f jM"BLc؁\ v^l|9O(0ԑG#A (\s͆ l^۷Jź2[$P -iI,x= !Fyh0wd-Lh7[1` V8@ЬWiM!Sckym Dz`­3㇂G֘ @H5sb &`D}fDvsW=9g)`Ws/ϚY>Ůɢ5 ex#C8zl.+q\[;M*Dkؒ%M/rEo6_{ӿ˗ ~`.gk`.ۂT Le?@@~|ԤIXuX0a`пŇAvmdb[mڧ?(tǵ1c9߲6qW K/o+';w.WJ3 R,@Q_ le(5p>bS&#zT(h][J{A鸦;M7Ͽg?32?tsŕgnw/Ҁ{]*r,,e`f3߬D5h ?J)'a{Hg'uRܶ6"V~}/j_<5[V_VZw4|@| x՘cY8e;c[[/m Jz=΢E%a(=B1B>=?W [h=]+{"TVlDfxl>O&%ɐddsv?q9-1ls LL6K.'ˑ|$oй;Ϸpi7FO)4=# h͐hpƹ~}2,Y'yd硋EB{l@g;wd2g{=ӯ9]/^ :z_Y_|_Bq.d9Ģd D?RxQ J0Xؼ(N8h4 E uyjo{9J #{9 tC{yDh`h贫q|Mk5kh]}k[̠a(N yZwMpaQ%@E@F993T} +47:f5^@殻ɭH.?(bjR<ES 0}s,t.jZ .@aݻ-[H>4K(0 2L5}(|2.8%ߺY"_(r8K `tN #B^xK)R@^4ihDMJkRA@](ij ?0W5hMoW1ݧ؀ ae7yǠF‡KeI_olm, ilxSF +k_ /BF4)R#B0hh|Qh-_ [}+nʹ{a`ge'VWc= ʴx8OJ!QZU 3wt/Icbpc#L_@UUQ]>PJ߇@C(T#-?}:W23ZHIQ"YjxHEbɆ\:=btww}ib-H@F"+E2JUۂdHa/c9H٧˂rYkj#L(n:6M;,v;//mS uZWO)|TSxaJjtiE%ǏgD"0V["uu^/]X~4P8tC _@Q C0WB0Qe,D6T&䐊j?ЎMqv;*\&d[8>Ց^>ܑ-^RJSS\K 0X,Ұy33n'( Pxr=QD(d(+0R"duɧ8:J4Y3Cʰq=B;9ںub Z#q\ƱmPuH= ^Pv^8#d !LN)x௴f0`ǤIl CI,#LOĉԶ4M6KGPegk3$L) W6 ׮%qcUfdrǗ;i;+MF *c ,5JQHv(7e2FT_f@^krz0Νh!o;:; ThRy9goOpܙgael+Z""lORXۉM98pe׭.z Q$;׶KuD8D, TbHiZke 8V|a=&0رM&I&$ qSIi?fI64L$I64,)\JeHfwȗa;R*ojz`c4^6ۦ&̎asQ=jld2I</)5i2LTf6.jZgL!ـсre[c9sh>O^ywb1)Ǣϯ'JU[>QlıP\sS)Dty'/#gpݺJr`!_ZF斟$0QbZI&$Z'10s: 2 QL&ihh L7UVA?@ڵtyd:b@3j'g :| bujKdt@б3@{xa\xn8[UE}uP)?*ן8imi޶`뛛~poxcP``SD"A2'6 U]7;ܱ_"8X*Eر҂|$NM A7}{^_V[#,]ȓݗ`] 7r{Z˵ J&OFOJضK@*u*"54:c^Y KoWQy465L&iL&qt]2{z&T*1$XBW8.!ױ*@!y0Fx >U?s"%_{*ECK3U=HF{F [vQz3`&[o9\1k -/as͐}P\nlZȋgBu UUa{=NMPj% ܔ˿i]6⊞gunlzϭP?RKW`$,Kַ,tOOs`VU=6cW{Uxifo~V~T2%<:P4$g~u٬c {G胫\K(L=hDD ĢR"JA@E/OxUUAn8sI%úis̟lEQGRL;Rh}2A ò^Fw¥K86-\xm8/U",YLiId)Lt Fp¡/ꗻƭlwgW9c?`XsofΡd}yܼO$ Aolǡ:{ᰓNLHUMI~9_^~͘~,hx\"'5HnC!> S`{ Yg.'/AS Fc|W3>%HH&h ϚE-eo(R.t0g6cpN\5u!KTF}kjνgF'~#E2 ;vR!OI$Q(wq͟҂­&ЀEDJ]l1?~RG Qы؞ N}iH6$I&K?ZPj+gImV4Z؄B. 78Pu$l…˾>/|[ǻ.M^/'1w>|yw}u9n]{eȿHJa]FC"no'  øb'(ث'>om}n{ya_*/:cs/~ȴn;S)5(  "   .     | 5S###)d   ԫo1>khB!uN/ @/b(C8O#'#0&+>- " 8  ή2! #$(*+dUU@@K&"/s??|@'"]))O/!+ 8< $|%T j/`( 'H}IENDB`socnetv-2.2/src/images/reset.png000644 000765 000024 00000010320 13040701535 017703 0ustar00dimitrisstaff000000 000000 PNG  IHDR@@iqbKGD pHYs  tIME %tEXtCommentCreated with GIMPW8IDATx{p]uks-?+]ɒHol % MM: ϙАǁ10I !i'$X[d[=g?ιbPd١f=W3g߽΁SvN);eMN_:uđ(PT/UT\AvEdÿ \WW wz"xCS*QVDTx웻wwoTn@fueeTM@ȑ8ΡaÐ:;;i-}zaݚ5:mrn-ﯫ A5A5O("]]lV X={= W'NXއg2̜5ѣqd{ќCUb#Q C;;y!v#o{/־ü%%8|0`P|[( C\0ֆ!m,MEnl{/g7/()];QR[[inh`_@stdAiF\[CX Ksg ]55e3T͕ ?Bsж};[]>O V`{PEƫ .I%":w܄ 3N?StPʶUUSe56 +#փy v |O*T}dbL yq @| X+yW:=45mRѭw'~sɷ9nzQٳi})yq $y}9~UjL!N]uuwZ{Rn{w{×VCE]]| T I0سn/ލA_东lU-~)ge <:G-0yX# ZP,αzFEH޽}||u~gdsppmsL;7A쾤Rt߭\Ɏ(ƒڷX\`\Y(U?1aT6`#9v+ϻ{8z1-W/,D|QՋTVW""^&Á}Ejl=XӪ•+36WF&frd",,_[}BToI.9@*WR%Q|D}wcϋZLqQ4i/pY3W8ia|ݺ߳ǃT.e|~S(drY%pY5NٲSGZ[ inkkjV8m~=5I"i/MOQRajd&p[.7Aܱё.8Ac@7CkoT+R&dxi~JO&T`&{\UqMa TTJ<HrT(B  !u՟y=\1R Hbe<*>VO6BFFM_" Ovc3;uvN[(=CpkUd ކxHRWdo{d˃RewLdzaéŪňň`$OI)U|zeEo @sQE>XőVUeر^I)RϻMhAm(Z ܘ8&'Y0J{U ߼0?h^]AP,8bfNy_\a%$m~ہ֮y8%W٩hn[Jbg ,c =Lf Ũ(E ,*p0T_=ƞio_hg7UYΤYaylK}6 o(8>@"gٽ3x4l(ʎE;T1_|f}n*_ e̹yQBB Flٳ?RLmoѦ/7#L19 s杅l>n. "Ҿ֒Oa E[XqW7IpΚ<|7D + !wo>҉3$D"W}k~j掽L,xRQuQ9|ԇ3Ut@ q(mvC&ހ(E&#ujx Ïٳ?WލLOL??#IcgsO#'{U%%o;>33a5" ("rFo$WC} C9L}(:GOb  Smg)D5ۡ%vGC;T123'SfBvG቟}8hS׺MGn-l5OI~y@ &s~/0)ϏjxA@ @ǯAuGӎgpphcWM=f;>5>67/ymd@EJ$ ܪz6vP6}ꌎ{'uTz\DO!{z'B!uiK+v{<ޞ$Xݗ[iTB>MT)-5Ex81,9Cak/@?{km'⎉[p x 2x}2IVuEDV~O{ppsP>rDTǀ8:k_JU{);e씝Svʆ@IENDB`socnetv-2.2/src/images/resize.png000644 000765 000024 00000002311 13040701535 020063 0ustar00dimitrisstaff000000 000000 PNG  IHDR M )sBIT|d pHYs--; tEXtSoftwarewww.inkscape.org<FIDATH[Legfwg˲\ʂ-P eD6yM->Xk &M4ƶIզ/Q,XTD(r۬n..A\Y?9ΜbfQI#z--cICN79VDhNY OP^2":-d/.t^o9f~ HEVllzkzuŽWzž翑.y/3dvo>)3tJGP/r6@Dzf)]A~jDp8FFo=̭b:dC*JV .>Qs "+uI٨Ȯ@yiILqe+<؀Z-ܑ[(7=R 6 zbkZ\X R#O(Gq@pv`t̽V>'QӸ҄Eִ$(30|Eņ×Q\̡$̼ 6x +!zok>JIHd\EVSUFi"%YXe+@Dj&f^4hnyٷbuT|03S+)w'odb` &̼AĚRY ܹh0횸qHoVxj353__-xɔPP74p_G>~vdu!X 3{@(.JʘpfP ƒ⍇b3d3`p,F()gD$ DT@fkRԂZ@ J}1kKe(i\Sm I&\vK>J\>F1jT !W~Lha,OnL/;"Gh"jQWBu  WCmvla5Pie|Ovo j P7OΎkf 'o@sٸ3SP-D*wS<{[Yhi(9rOF9?\non+i6$loFL@=vREf~a6IU{7@v\'E}Vp& pTg('U0S%D0lSmn@ɧ=zx.Mv֯LnU8IOЎ`/V1{[Zga2TUՏ5C6G79uʝ)4J O!.D[mŻXś=:{ʳQ[0!`M'C\!~EegZ59P*T, rf\Jy/< ra삮Π2JV84h@MmFfUڶ+)%w֣\c|I XTLV58fYR&FU{KGZELeKF+:`3,C %jbClHv<μ<N(-jUjYGoe vMb7qeY#6Ė|I 5JscCV4dC|I ˤK)^ 힙VCyuuUF~&1#kbK|/alj.V;`;Ł)N*Ѡ{@dΰFl-yld?7ztjӎ{ dL1v;IENDB`socnetv-2.2/src/images/rotateright.png000644 000765 000024 00000002726 13040701535 021130 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzIDATxW{LWlf[DpJ`)XVJ C%VDA- ,E:^hy W@"Třĩs-.e嬷ܘ/Ztkrlı8w\ѣH ?g_H ny?5 w?clGa0Kd]~ml*6#kgg-cH?G s_!KGL$#/V !nm5\>k:"3|B+vbVD|8Mzjn Q-/ 4@,{t 58!IBܤɨ/anl4h~o9~|d}5!5"{\ iNThqVy)J.hDx@XIOD"YVA17 ` Dij4 u# )E=G yȕajm>7Ѭ3JO =VG?ANmySCTv,ڄƋhB.4{sLe Dԗhb+l7 qZ X\.aiN(dW 8jI5h(CaӮg 6hi n|cgA<[,d~#jCrKB~[Hj^1l~ʏ"jąU_}r;Py9=6o1zqK%#ĥjij6`φj(^x9$,"i<6|$^9oC(:|)2`̝n'nDmAܶq8Kp]e-H@KH kȌQbqrsA=B6Py/5U~Ǫ2Nz9SF#n'ojsgb#8WP1~nhb|܄vϷ '֛jiYIv%=Ĵ/NbB~ .q̞SCfՅ}ݐST9JJtrYS1 H:Qٍ<5 R:3EJ}r󠉪7.v}jjˈisɐq]ӰcϠ,dX-b6`=7"uVYlu23n BX%%jel>pm0qG TV1i%T"*H$GI %5jćDa)?+)ô>J AMe:(kl޶q u]& ! 1#9RCj %D2RʶҢ~ a +3. Zf]vw(PmvOe%4h:|u\d5ph g0`UhB>N@b2f̑RK8ՌiJl\e}ҁ|"!ɑSOH/IENDB`socnetv-2.2/src/images/save.png000644 000765 000024 00000002243 13040701535 017524 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzgAMAOX2tEXtSoftwareAdobe ImageReadyqe<5IDATX͏TE{n@FM4DHČL\.M\kXÎč΂htA\ =`PQ` ^` QVR{:ԩz~6>>]vN5ػ」;v<:̗7XSڠkg|p?0x.ory #ӟd}k@E@xp,#˲m00aP|#%"+Օs΃* ,I Iw:琷Ղv+m(r8 㯼IkFؽ{k놮ZVmQg5 ,# /#&|#!Я'aa%% 4M:ólc V_)Fara-р: TIO4/!g iXvj#wPE x:Tx3x$wh6h߱R^DM_ 8ňKvRPZ0}@Ű&WU.2p}Ʉ-Jsf+7QW?תd~')Ü wl 55Y1{X0+WVU!ѐS,-޸Wj'WڣU<_o|w__sl}5ځ?MKFIENDB`socnetv-2.2/src/images/saveas.png000644 000765 000024 00000002161 13040701535 020047 0ustar00dimitrisstaff000000 000000 PNG  IHDRĴl;bKGD pHYs  ~tIME.*'#IDATxՓk]Us97KiK+Z:+ gӂ8с R,RŖU֛>ҤI{9g~8HL &koq>d< js}=u飹Ǐ8ܝ~q7vՑW5ogN;Nց>~H\'x$#M*Xk1b׎׸)Q] vʋ? /wi5}wĕcys<0k/ d_^'1@ӳxu҃77MPRN&4kd[?JudZ v/1;tF,Fa02yh7 # ;좹Ph ]HDrs:>̆ޕfq .nzc4 u F,{/>AmpWT{`v^rm/KC忁-"VVr::uXxv4A Ը8-|-yRkUiq:<6vo\BpRk`6\ba}UŦ˝jmG6m `zMKhmpR &Z0"tGpS5l64 mFpȖ93q|1B!b*'XV?"K\#E@7!n5"Tih2@k-8BS/07FR @!lS=13Ac ;>1b)CDIP݅2۱"OƄ#_;^'Z Zɲf@ұ4O64@TD\>S̃,%La#Mch@=LD)M+| bq# w$Pݛb#Y|R ei{7r Ww3!ՑBG[k婢$[jaT*JDLmJd ²9H͗åosLJs%-+_B5z*@Z4F@rJt*`ҜDpĶz :¦-I wP}lz7|:Z>ig{RHZn711" CV?R&_}Qѣ7Ƕ aG6S6n4>ݷd}Z̙3gRj~s+kk5w?U}!$1::8p p#QX8-"1 @pN?*n_ٗ~9֡zE=, H1 c2x`i+-sE"Za[DZ\rh\tqoi-]doKZD~*-CD$7S @pBȈ+[ӆT 6bNiyEK^pu@ dr4olvX]'V .*RX:@m6߉xן"Q1RSSZٻ än=Y.N`9;p8ezHPy"(+{ėOS69aT< tr zYXXRbfzVV4M'0F[0NX5m ]u~CN % <\n(جUiõ^."R oyXg;-1111Ҍk',nXKNg/fvH1PtI#D@&mRG0粂9+N)ߢ$i8r$ݼ5/we`eeU P@60E3 zp;WLcc~S)e~=ؑ--v̐ KIxuVˀo{ziHY8+\ =n[G_5*r xo_)~k`R*8ZˁMMkg%v8 V Ϸao7rs/E#0( tzw^{8wdY_ ðRC \.z}m9`Al[3Z͛? ޜt:AF4 ݫ̪M:,zXBcˀ@|@ʺ_  `n@o7ޒ/6\j'V \j.h|hl\a7j?j?W!<=c;@aiv`)=I1MSMgvK\3cT`uqۑDCIɣGinlT*VJӜ %@X.+6|8f6u՗wSzo%2?wٳUog. ֽX8ξKn7̝L&;3{GИ3: IENDB`socnetv-2.2/src/images/selectall.png000644 000765 000024 00000001721 13040701535 020536 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzbKGD pHYs  tIME ) 0tEXtCommentCreated with GIMPW9IDATXOHq?k[ҠV .zRT%K-$RHF-[FWii+Y,6m]L<~7GdJ.@?tqV/%;q7 ) ?X@Gp7dbƋd"A0 }zֺ)d#-.dǎ$ ih2a8 ҅r xpz3dSqߏYYIK/THR ݣhB0PR)޻px.J%ܵ> bKQ <͗ ys57scq2##d'#m6)4r%xqTl<?d=meo]Kr|K+II^#Rc1FDi$oIմuw3m覉mms(6H?Xj!ΓioFG[Jq Z[yo(!1uu{iB(DΝ4Y<|ȽNƤPF"НB8#h/R]^βN>ݼYdC6:45їL(( @93b*-_NUo/Swu1Y֕q_z9XVWE+-gFv紨yUwVg }F4m5 mE6nDK~>hIENDB`socnetv-2.2/src/images/selectnone.png000644 000765 000024 00000000766 13040701535 020735 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzbKGD pHYs  tIME 1ըXtEXtCommentCreated with GIMPW^IDATXJ@wA<*سҳO QڃkТ%6^nZc7C~X2d2vhp@(0PsdQs@h3YRy}8T|V(&u@ $_p`)vcDO"#WRbiS>>fgff\.JUU f|> `IIIIWUz3fggq֭[xߘŋw>711 圜 z`OOOϹ[>XfMG xPSSsh]zPII{NMM] {b͚l۶mQlΝՇz``f+|R+#* l $ lb-,|vpD؏$)30&;InIdCiya@ąIP9CW#JkdFفl)H ,C"6 xjCE5ב#GKYyyijEἍG~\ZZP[[.]zk޽z]~ĩS|k׮ OVo޼yTUUcy<Gbb pݯ8Ο9}YYYv\=Nhƍv˗<))vo+VNJJڗM@ }_Մ*  066hھnǏ_h4&eK!`zxx=ɔ<22j=ztN>x z`0|}^^fݘYXX8&kٮvuudVwGQa ެڵ x=aXjF禄Q k@3>,~"kъ7@!:m&B>c/A`",]za. l⇁5Q٧c`\HU˦`Bv~>\yvIR`XRTtfe˔t:]d2ٗ$W%nIɤ<ѯ\jkkgEEE@}};;;?9vJ_;zh@KKG᪪]Z@݁eeeo9sfՃB}}}KfәfWlllvYFIәRSSmrf8h4bZ=^Wm6h4\.Gt慸8h| QM822-[ cǎ&p8:;;{{0Lccc-}ދ7o>bW3$C2D Z9sp//ɹlL^"G<}aoK= ;'!~*e2* +Q#YtJؤj0%7q2!-{ ^g藖DTgіI9QKd_x]a2IENDB`socnetv-2.2/src/images/sm.png000644 000765 000024 00000010320 13040701535 017200 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzsRGBbKGD pHYs  tIME &G?tEXtCommentCreated with GIMPW+IDATX  AAPPIAAAAPPIAAAAAAPPPPIIAAAA ^ ',yIENDB`socnetv-2.2/src/images/socnetv-32px.png000644 000765 000024 00000002712 13040701535 021042 0ustar00dimitrisstaff000000 000000 PNG  IHDR VόbKGD pHYsS`tIME [zWIDATHǽ[lTest:m4KE J)Ң@J1%<@h / jLM`b$R-R%(P^hL{|8^HC8ə3^{?c0HdtUΓ鼰2?8]~UXU5㒗}&zYf5qI⧂.x/S{r;*} ]ABGR@SoqF0P \0M3#O|Š Hܭ1q{H+zLBfbG "JxJs-6/52ONK 2omP xR`oVct(ژXWL%Q`2׀Iy+wd>k$ÙMy,/N!|0(Z 7`&qrk+WZF 8*a]lWj-R?tӁB`sBd,}ÖA00RV@#P%%"0`'s MxZFo$4sjIeHD e)܇?y>CRka<Ӏf[3F{X.6U-Z0XǁtɓxHn4(+Q'HGDP70G~/1GvP;dT0pC*26;; )[%9.A0̆x*8akHl nPNԡx0rH~% Tʎ2K: (Yq#"bRt`z^ T`/P4k^Q\*h4#C#겝N Q)Z/[,y!V9LEdt| u=5P#'t6Ixzf yUVh˾N@ jeZj} @)(m3Q"̧˧Fc2XO'oo0"eʣp/lV :?NHOځJ"A#{ FѼ4Jz :8G ѧudF +Wi=΂*-!(Q5Ɖe6V&[䧺aH 1%$A QI7JjS,X0ԁz3"S ݚ(TU?b^Q <*B6 Ή7 1 _VM-%j57#NB(B@'P"@&D/0(/N#_)vɍm3,(B5(b$ !.>#h$7(V,ϸ! 7: @Q|a.؛$$ FY^'};Z? 067QץȁJ?XV{@͆7c oqlD8zW4ج"W&U@(H *_٤_kr6TU=52xqm2ӭIk/)TRۀ£µ[? jLSF&:AyVp^\kQRF)n,8z+U(h d`D5cXT$Ɗ{EJ"A .L'SavSj˞|;Nj߀wU'm-M;Wb׷#栫no{вSUx2{) ȍȪʆ):֪;)r &r~^=Kq+8.³ 8@.BHd{׆Ty+'^Wg;什gׁo^l!ڻB@ǎxkrGd Z?,z]tWE,b(@4H7 $pWeAh2 $ER% %Y B @4Z޳ F5q{X!V#Z=NE?ғ&k\'s_<\7 *?R WV^?S%I [g\ٿs2MweYdsIO5c Vc7F-( +"U ԭ O6L,]6tJ tKmc .gdaRХjmUKeU.: Un&kOq@*W*'b! Aq\ i'>|NWfSV܇,:= ۻ,Z?7,L|R3kvmao} eOCH>ϴ/1 _sǿFǵzy4^&P EJdc FY8Q)ZW7YE<'߈LP5+}Z5om׾r{{O?e Z;~_¦n۲cUbJgW+/:}۶_#׻+L HQ.:5z?2jcu NU;ƆD*|Uarx֜-ag%.vNnNsp$FGu8Ah[y!?{ێtHJ̶*t &Ufupdq%'v$-lj e/1T% 9{} BH9_$̴zc-3Щ+u:B 6vjCusnVD(⻻} N>SuK.09` G2jiɤ%E!:0G.Xs[x5pV7.7lj)&_ 78(F4 ^r1:v ._f}$V }36%gTmthsu &H U$_Sϡ+\"3^7|xL/SO|J{vqm=P=K31wT7K_,Nof5Ax?)eX[ޙ;x3}H]Nŵ B*nZZFο|{<\κq6q9 s$ԝl%7]϶&ֻnb}DnOԌFft[omluv}ko\PoIENDB`socnetv-2.2/src/images/socnetv.icns000644 000765 000024 00000552361 13040701535 020432 0ustar00dimitrisstaff000000 000000 icnsTOC ic08ic09ic075pic08PNG  IHDR\rf pHYs  @IDATxg+UAP ]D]-$5-1Dhbb7EFI5vXر" JS,lww^{<;N9s̙y{%=@y=@y=@y=@y=@y=@y=W_ %VPCaeeΡCAQaBu\aPeE(( aAP^PTuC( wUHXܨ85nɚfq b6ǽ*4-ʯЦ/CTRýCeBuM/M G˂o}5~:fBi(,, ϟJwv(^Y; apơlBP]Bk.]k(o}Lwz/n[zt{gjD(x1)yx _DB{R^!\;,y""EmĎ J,yRz5ҺrIaތ犼`]@0@ś~:%ĕCiYI8et@ `^֯70|rӡ>R[~eXhg{֟׷z=XhƅZwyɋtK8Pȹ@]z(C{%9|+ӔʊB6;;h 3}7#ɕ,֊P>g5`^U_0S ~jU^{P:ժVr'}2T3z_s-UCom\[{ Xsɖ_:?ٔ/5;]BZՁ{ 1E{jйda VjzQYB/XIxvp XѴ&OZDr5ɼy ηaauu(P{;TP[o3mGk׷mV0n|7=Y<'/=SX|mmq!Wjtv+ F3א· >Ty |Gk ocX^"7sN({|Rfxc͘neټ"T61 eP0ݓ J_.2띧5ؤũ=e@xXNmI')lTWw  UV!U+OzLox_3˚:\j@.^&)O d{қT\m!Օa.E6$4+:5 <130µw>h Ҷ= xqۊז/ zPp0q!{,(x?"J6<ӯB0yQ/Nob^{sE>f\#K@~4SPq҆$@<1 * ( F $Tgkm~P=s(+9u_~1_<쩍 zg!ݦGgAx]}Ww{xoտ\,z Ұ~?S˶!|W ڇˋCu\ie0%i]{[/+G <!AM>$n{axcs7->lJ6&~lFhQOmc@whhߥ_WšKй˺5:8<ΰoEAm&lk'|h)6?Mol@wylM:Me#:|r]q Mr!AC[9><7%{</y@(cu`(c<6/O qe&vI%2kқ߇lo W iCY ͟m\?ml8@uicH]?䘼=mucs)_u{oFq> 6?O meԱYٴ@7χA;`|ۘ>GEy{l~67 ;777i`+8(7+yt\+xM_1R /xAc #|f? A_>6荬.^_/Z# eqc @H] (-o=)>*J^V:Q#hg]@"ŇKbu/opW_}ZEbj Bf.H`U/~ Br}-^ k::1Wr@Qa˔Uxדy.T#^[ 877Zlbg78yHlƯ 6j,q۸yF.' }h򔡤V h^(~!xӃېcƇU\[OK"=<n:L}*,]ætЍgZazqyx]A 96bk4mjJx,S8uFec%z^ΕPΙzеWkqiཱུ{ t[͓}1y#m+)l ņb`!iϣXU3L +oJ)~ybFcY;jqaasB^ A[%.÷?N٢KT`IrCśIGm~Թ)zhcÂ6/5!HͅR@ЃF$AP?*N:)O<+ 춸k#o.ki|2ZC u^L$GS|Jc?TO{yBFjc~)mb'|)7Ņ$,Kby;}SOBȱ[N5#\ðj ϔ\l(\9؜; =d&éuk6ԦLCw|%X:{IlI+ DěsFrqiEƶq_ǩ֊41mۉC~I!'^N<%ecqazsII;9bP8P5hPC^M5Ep g'OV/wahoZ7|M}xl6{ 6#OO 8 FSQ2ڂMIʛ/#_@nX,8ϋxY[2jщp\t.˜;y YWtlm'K*,v1}e-cm:>sa]fvl g]o>k8^>g`vnOZK%⹧fjj̗4d X--ӧ>^mCWK= dAnݺeag,*O?I /G7U@wG'eG$-O{OՉ.:8>:^UId2QdH wq sbLuJ_h[x<yGe&MS: N(vjpQoVkl~as4 9mmcBk~;9p빡pYIٱ}zA:p꛸y)CoJ.$lsąb'?V~ PQ&"xRxN eFUTWG J5L!o$fIiK3x vi(t[_v),8|x{D}JZǓ0|՛ GEMՑ8M bLߧ~L:[ZTẎk%oh98HGs6d]}b]gr\&{]|Sߣ<'-†fҟ-)6wPf>SVAоX(BȌ5#8hz|q +y6mvza0z']8ts,v_R6C,dR^7#n7vWʰqvunz9QA0sc<"g}#u[qMA8C׷X:Kl,>u8EcYl*65u)8VzA/A=w 7)N+W)}A*NI]%lž^a= epm;x\1ffCj ?[m+Z&Uμ7ek0X5X;^o'R(Ć-UԈp)NESd|)^63q KCЛ&cotJ;U}(Tv[QBwZQ+\LIV:*bǵVТya6"GA+]EnYzS;ݷ1kO?q?[Gryc`E-Pa}N9ȓ]dcwgƲ>ecyIA=?yxl\i;Syz#qX_*kcq}لo%oDS!P99Ub G#5Mm:;79͒ OGs|"SmwN>]_Mk 0}<~ʳ!љf lZ898Cě?|8zƈDZ|!@aaMoXs/&x/αyևOcR_;ϵ*IukA)ɁGiVv[dFhJ֦DS簇_;9^_9wP,DC)n ii*|Iy^uw3SQ{b6o)q۸wkφGtV2]T/io曅r0DU6%?'O Kʋ +Z]D =i|-}K=W梃 @X)<yp[v.|Gcr%C"YsU%Lk`6/H cYOqM H<'n_<-лH3穃Á}cӾ]#ť~~e3(T?=T}1|0UXߺ˜+8͉ P>z}G/Z+J5?x~]b(`,RC깶/]t[ս x(Pk>8;PlVHrl[*^Zد M$G:ϼ6nq8KmaO(%M elz- qUpx>{ɑ֨l԰9,ݦzYL Fns6iɊv{ 6_:\a?.Л21Y?lӢ xra_:.55T|PácPRw׃yI8s/y}xC/T`cL=&P6o}|ϭQwdlaRq! BCqhZ<*' 'e g tA墓X's$Y&$6>$:t]'|$\} $ c?_ ڞ6/ztzT .K4Z0.^ϡc+j\&Cqdp7"Կ+: o*FV\EOl #{<4J9F+3M J拝cTAlp&c_ķD~#b/vG9LlǪg>]a%?}!Tl1?/ '9 %qzKjF5mO! s;7+_E!=Ib}m/ /=MO ŠUI^~'1$6!b'OşbqT'ADS3MyM1Ru&6o J 6P=Fĕ‡=tI?R 1Z(<"E|0>0Ovi|Aan}ZUAC#- 7&ԒCwO΁ᾡ{Y'uiFfoEf *.}¿V= Tꛂ|ϰD\T=#Tkh(߳ jF^iVd "R֙GgT'k;r8UPQ O0'q@,^gH@<^(x|G ~s_eWʩ/Hmhw'6ѐ0_o`$%J#wŋb@8uy zty_ { ZBA2ÁzIjӦZsb9}~jii" ^:| Z<ʔѐѡq<^>m.ģB[]O?k_|+始lR6A[ʑuEGARu@)qx̝S*>/ppI{dRʨcm9<A' !OJ ~GI cCߎ0OJanǜeM5R<';\+.R0Z'ABCJ=ƠÂ>^ .NnuHߣzZ>Pԥg(UH)hMkq8 ?@)w ;+B"@AQ7 O@ӁN2N,M z ҇. 'f:YpG 6ATEM5cKln;NpphqR ;>g ?zWNozҏuI,*Ju9QRtƾO ?ZOo΋Ƃ,AA  G{+ = ƣ}XL@7Klt)|JiA;XOl/6z˴%&B'62l"v~zNq`_{ l'9ĘscM/M(zxYu0Vcem?{ 8嶃Xח t3lOT'~)m?qWAXXŏx`8 +(x|\2~1q/߈ AYQt'-U@vb[:{9Օ܏vΓff! Ax([+cSqM#X7 [<:Y_|*69}m<koY/5Gq{]%Bp,@@(M]@@O|ӎ;@wƢ?c}|d ʌUQJ/D/Xl?O܍gKW` c67>c3l,;r}UG|#NaO25r. ns*1~]縕!ܥg I܎Py=zN lŇ^oڼxPlsMX̝~Y&u iAwt *rR8UYQ$\8Ǥ=XyO+IQo(%۹(>Zui:;a;^!6B\/hOS06" +)X3C,\{&#.3^,Qy&U/)J.SǦ#(}zAUI  lAQSşg.w3tU\'^,6;%q9XF9NSOl-.:@¸sbv*&+_(1G EgcWclebC?=f,Lw9?\pHl 89|=5NWUX8OG ;õ@!0Ҩv#' -Hs۳,wœiME{G2Rty5}iwP*щOi繐R-W9qS\(xia`  |Xy\l`|6>]1X`+퀃SƁ[pp!O tf=[QƄ `!-, IyK; 6ωO@z7xUrFł1,SW_ t,6Q6i1%vx\}ҰayJr?uJDR["x<@LpXa f_AKv+ʩ5|M[_Dc}gN[6*JA Ϳu>Rp?D,`M~??u umFzP׉΂]03fjA ~a>$97Js/y3n*_/_(7v!><'6!}@ox{^(c9(X)?v?y.#r—!~L!X,C# vO ݁@jOƛ6o.l%b" >G.``cexP-AÀyeWdʩ>)8DGF&c;9 1Q0/量='e-z1ls16?M6ׄkڢ$oA{yp1nkEj{ 2 |Y`X,` 8j;AM{ MAP$L*v$m56žY/s9r 6󹂹B#A?| .8_o8^0&t`WlyfdZO]皴9Ƈ'8^92o=ŏ#t΀;e 0Ks(8 bQXx|Ap)@O2`X`#l6/ݐԒ>WσB0mRkڒ6e<>0^+n ^?c0Rp!ƙ)blm䐥ܛ{.3)/XAFƬ/'8/k8"N)u3<$e'{P㇜dA ^P d~O =de ,t8Tل Mag80Caeڻބ lF%n.t/ce\,@xo6݌AzfoO>R^@8^`^)ɾ`]w 9O{C=~y5]$X8NbDpOeqE&RڤtA`y_I?l%@I='h99-GD29P/~Һioaئ8O{vy(=2Q8Oۺ$p'q;[Js &4m#>oOh3tz0p?ek-wl<[rB! e9Ǔ z D ؾ[z, :y4`<)huJۙy2|>R0ϓTSRO-m n.-І9 z@Nd?6_EW^ZLH,&CPqȁF;E#+  rlt"lz3} <7'ذ=s\x}_sUOiT8=Wx,e0،\{e o 6VCwXGEY].c ܇`q\^uCP0XlK+z8',7U[h"#LbNK1?Zpc׋m#@/"x2 z b=c5]TxQ`7))؛M<7RƻM؆G[_GRZ.|xRdBlyeg bH ul?TA0:O i[K MuHZ6!utF |r>o~ƧKoELXܺl`z(3b\!8'/x*`цCy3|^'~'_N2>vuR(~+8e}N t VۉK <^XzgFAC`q7{7 "+&^h'A |X˨ga^m/hw#& dD?CMA5_qz-T7.E bוX;bLQ! 5M؊1a楩bDp|8B<*8"Nu\X|]x/ s_lb;6y"\`vGKA.sr>)ާzWn_|;-+)jwL#&C͋Okb#5k@_`- >Hv[_YcTuYGvhnr8/ ybk  wd%/SBӖȂ^'ˢ2R^`qJp^6e q`|N#̱1r`c&^7\_ | _ 5OuZќ~PG0&[<崷n8ͅgRڱΎḻWq^7b7uX-V"(iF`8,0yJ,ccxarȀ]Ɓ೭>7V`?v0?+yG%*? ږ7mGtcĺ?|oS݆:|g8`eN&ClLMC907֔h-9FyޔG:ދB(HoA,WQVff t](t2>fwPҟg=;w[o^YI}w|Oݤ<*8h(}&y:!ArTk6ևsan3AmE/4d&뢒 5W5 .,uaX${ ֋-/䕬4,A7w%c>F0 Ϸ\yBsnLttk0s|FTG[xBOn}r- ԴXׇϛr\A+n^|M=E_^oU^/Sp-!(xFp6ds}  w@hkI^bSᢇ`/iq[{x}X S8N6t`;v?.8l8 kLWK5J 0gR6 wគvD7yǣhT|"93[@IDATHHG"onFɴ%|WPQ}nVnHX) *Y#| ׎!A ;+P?<)bؠr .ч=VOp@CǢO<76>t;a\ OmmodRlŋb@_SFJ,{*qM&l;;qH[ *#w gm?e HLe>؀Pn~cw7a[ցM x_ؿʮl*J|N\Q9 1"d ets# tkOEdlvS/,:8+φ9A,pdT[pEw?ħ-0_ HgwI{bn(sܸ}3SO)c욹$|O=fo}O?k=k!K;&%0&7%⍾C[ .7ʉUb#A8c}>ʉ0@!c|831[GۑfW9E܆PL<|_\!8Dw ,\)mHm)8X"~d!Xa̗hl?l&y%lM zA˕Md/dc^<=_'+w ڜ%؜2lPe|vƿO*0B)1XG:>5Bl0ar,&tda89 FXxD Ic8a@WArq86 [ vm?D} myב2>{c)[+WC Aϼs%fia~vp}ow1)`c͎]ט 1䃟zM=bDG}%ىG=s<P!a2||#SÄb'9P&b=@#gx i>OPω~#*ZE`!\-;;Eהa306մu9 h`iaCB]ugrn >obo@6U~z{Q7VL )F t'+u5vb#!A_ G|/ >fuNJoXD g831X8 l~; ;4e;dtxTPA>Ga<=,ktPgR Vt(ꛗ|t@!cqp\N[:lW7 Gطq?B*õQNqRG:I+.6ڧ;mo tpxnUQېvCaIP\\J c1 0i&Ť?@02a^P/jډ8}Gy'2)ĉ8}3Xl#m)A@o|cl`C] 枖cUbiab@@8HqDj=Po IZSO;-fo.A|TIYqq%VC}Nl3?O_ۅ]Ba>͞T 19bM, F:p :wOLGvZ0b3/dzsN;ǽ "wX_ o80I9@c-dnM{񰑶2s@AUn2>ȅuf mdxOGYmy @k=MZ1&Z9r-*F\%n8n91hǓ=] ԥgre.Iݠj6S10lp׀{{ vWʆ )YPZp9Se>i_sMX\+6BGOIMOg()aͽ'|PO!}&}ӾYWa۝Oi_-^cea{Fu_q5'̖c¹ n+O, Ux.* U+4PLCQIYm&`!3tT|q eiOBA^͂ю w~>Iۢ[,* YZ\*6@q!ɳ/'@_߭v'r`<=2@ :~(+tSZ w6 W O*վ{BeVʞڗ[gو'8Hvq:t_^Pzz_ *%1*]GŠ  tKCjFJ(EqAl^+!,wО"WU"K9Bбq?.K* .E%xp?e7ӔCPL]q;] ang_ 2>A56-. KT& #華w!caL=f*Φ=|Ku\f#$ *T[Եw<`XطC{f滾4J..%w\=딍w3&*Zպ.֩ QKMaQZV['r(GhG7^(C(\ǰn6c t)C,e}Do1Vֿ=$B i+i旾DЁ*ɯ?Ï*z/lL^YǡFj ,˯?~6t|5L/F)Co~H\ F )_ċuUǨqC':Vm+zF! I5ZWS&q|`{?]];yPWߓ PMG Q CA¾;7\)9B/cuP(qP XC3 5)=Ñov m"YG赤cLJ>RDzTFmqݛ:K0:la O1MO]\[wn<p8)X2Jj{{b6*AX{|F{ʼM\Wʷj.kP^JAO6|A¥%aCJOas7G) 8wk(XOH@`q+BR~ ?R2),N^RX8P<.y x_tk 1Wu A\X9"Acڇ u}5v[R7fY'%nC)s)_'y}2]u0~ħ߲J>e@u!Ckc/D,^k%/s'|pZFt\8ɩiQC)MSW:c|:q\E0]p^vgYTlV3K}"!ݸ .ITuN.c إ"T380 C>WNչ4O'(,S)5]N˶)㉀>le\sih"y%D]IʝhHЕ4#׳%1W-/v:~o8A>/6* qZsOےsv9뵻UV7:6-x@~/gqd*[:NX+)3t/mf8dA'6IZ)H|Mb}qN<_,x% 6)sќ;{Gg^2`C[KЙ^h[TF|'v y@k9yA,'vk #vW6t1jJqj}Oovީ#+BeaC17\I'E,Ѯ'v$g Umf--!Z_2 ˗%F0q e%pMUqJ($'eyG#}|MclOFê?2~S,ސ]Mgx$( 㑨6'O2e.[<}ƋmfxRmD>riב㐶ڻҖ> s [U+l׵kƗd~A>Xôwsla oGShʖ~mq˜Ҿ1G?C 1\vKP]&!VY7KUK68px}SFݞ'=lgm{b(?[c<;)g94w glAiMi?S\IUE0Ntn^N>'‡,@ƐPwI Cek~2v0M}QZC \ם6ǩ?Vwn,[JY.J ׾Mk^ 4֫ZyGvFty6F?-?}!-{<۵}B5Fn&R>5C,N񺣃MJZx,gIyE@1^ A(} #N Uo?,ן */O S <'s_%em،Cq} pXee7T&ߋ#EO}XY` "sy.*yaCa9Bp!ਿZzK$NHUËl- I|ꪈ9Hӱ^kirޝ2)J~#nc/ q_7;i{G.[[ᢻ3$w*n/\b .Wm}8-F[=[) S_"2jaaWݞ-C+sےMGŃA,}ÀEZv\3 \OxhKEGpWݻa$8_cӞ]$A9cv)Jk$4-Jxm]-PѤ&K:E ~=^Cz.8],> UMe;DjYܬ nfœ9^Pז;[`YomQc_AܹX>Jnp1˜E5hiCxͮY_8ˎ'o{7:5-WuvJuWuvib*@{9 ~(tN=r٤nK"NoFc>wHWsuZ2.'z4-<~`q! ?k4q*^S[{zp1 :ӓS' F,6%w!'`X/9~ƙme֕:D {OSݗ{l4fqٝ?8yftдi?fouKj0->(qgjs]7Z;qZ;Ƶ y.Cf \WʟDlHM۩1cc24LmSrCೝ}JeڿnĻTz -+ 5e|_PkQwS؄,yϻ 퉵qź>lv5t#k e_5X:|Ft /9`;&9պaYQkօ/=ͼPjTpq(clLy}aT [߸̕R#^/ Bv\*ao`i h/UsuI\[[A`Ǎg,@J`'%_C+/kLGޮ)w5ﷰ 6T. bni٬'c=@zTox|&庸68hcd7XxmL `M`\1gN i$tMuyT~| 3eÉ+oj6;j=B]5ޢfؘԉ xt:'c&wNoīwp?姙>씺#uxjena>| A~;huoWkMֻ˃zM|0|\wPdiHiiIvÝEndG0r.k8 ~J%1E[Tz\ hڕuY0=XF?K$p* c=E,p8'D顠/syR\nShoJt&.RX\nxvPCI7 0·xT:ވ?#9K9u}Gj^;{7/+JgG/7Uύ6yL'FI|#_,rۅ]R;,/f+Pz7bې6mt$(.~ 7l!Ȼp߾1x~e%u^2xmNɓfz k̎;o~uws]/˼I\ 7)&tIpG1d+~,q+{cFQ)y-3nu hHꄛn7]ۧmM-{KuzT/J¸E[hiG[?6lcΐo/n b*$ס$uA1IJdRh f)i"-[RY0(֢=@ĺ;WH1.>ܿg}޵s\çBԵe_}"% }= :%צ5Ύ_;J :ncЙaptV8272:1.t,s CzF`Tq|NjĢ}>g0yC.᛼li{YIsvM_bMGSvs7ÿs}@?`>`wQws(wσ8lkܔǫ1ls<>fHy}F8t< a x :0v>:ykwl( ]<ׅ4NgWw0 koAlOm:)rSu:pᕼyh~M6Ц% ֘i!1o-ipLr(~򎪯e .wua!φ` Gp?JyH[J1Iכ*L$XGg%cu'5Xuؚ`'''wkqCXatMc,Ap1W^hPwɱƃnڶ4 ,mO_Tgy{WNm\M߀kY%4Hn.Z6 ?ŚEwX_g}4|ǃ]#ep]]u~`y^ǃkhjv::b<,GL8Fm{4xx<X `:P1ǸzQ,|ym`WA0΀[JlKj0au~E>l9;3j>jQ|_4?딋J1/S˝o1F1΍D X881.| x\C1C5:lq~Dy6h2'D)nN7p ڑ-7$0(ba`~=HN {\2]2J׻R5HiȢ횚sX-vJnݼ!ysk}soN:ӆo}o ΰ$^{z0Np'|c`B)S@"۠͑F:tX vl F*? ##,98㜏\nVU m_?eopcmOI[]i|ux( Y;C[X9<@kk6jӺp8ls!:qaZM"y> n)&v\4/b޶|p+w_wa\E032ˠ1ՓpaiqLT1όVyōlctw}aU,1oHqm\`p߅g@ywwx![cZuDY}Cփ-aG8NCE:مpC+ +w7u7)m)% Y,-ipϻ8zC_P*y^nuk^Mm:_iG㖗Klq~ay䄽t8~E>pt>aO>qkCބ@swfW "[Ի*D\hpXV8uh_AMz/mp4JQBmy;X W>m\|GA.ي,\хQN{-n0`sp<.Fq! }y>eL-ru?!A~R:>ZjGkX |XlSwoAe[6mǗI䫟*i@ ;+X}>p}b"/@n48o&Y`W q<~Ƃ}uKCM%(ns!6>=V1ba[nxpS˵+sNݮ-hS!|C"KX27v/o"p\X7W"@B@Ul8LkS\3ob9x?>qG{Rq`36(q I1 ݦhKR;|;|9_qS鋓EPA? ` [Q_iO@]sNm\lc nA}*3K[nYP= \KA%x7uDyޅ {>D5e3iV쫸\q7O~.8AlF1ʟSE`OaGDQ\_EF܏6Ll t{Z# |O^7Mukhs ڬ/s?F\\l\GnrwmBy7Jl0.FqF\uh3hxU"Os|w,u~ mP6Hp',WO Ջz RKNj9P;»esK"fcݎu6SFyLxPgMßai56tֽPH: nڦDZw蟶yGk0~k8h[؛!AӋkuCm{/]\{REy@^,$.mEibGz0un`876h x9:*⣤r#hk (9r-Y/ MPoASb\(>KJn߁ ziH,V(] ׎HOqcXDj;ڐ!8nft }8=xG*5./ [q6x8*y NSsp.-0.@Otu.z NrsWv4/`}ܕD{Kz@)g¹`iS0AEY:<}nZ7D ~>W (Z5$;ncyc[cF 8|x;X3S}mu'?87i^Nuaur :\`Gps=a ak \u1 7ͭ`\:u_;#p0 F{]R {q(6 m11 nچ~SQn*Lxɏ%f`;u苅EGXr_[_kTBkc'2NLu&1*Ҝp1: |Ņ; хr5]`l= FQG榨^¦&d= VhR n6sqPzկ-  ?Pc۰+B \rIQ~cvND>$8ˀCZp^ _%Or|OpFl.6w}>`9ud$3m Ma k]8ǡ_,_`x `С {>)N;̇ibAur,v:XBw<.60]t36ľq8^s.J>;qfyCLP7}\?3q8oqK\Gj;Ny_۸&MNwX2uA ]iҊWG;Snr:M@ۍy ѿ'Ă|QcL \.j;+0ꍇa\ >VY  #4;ghu入k>6l'n ~ dN how87! n)`e2| _}avMէn2|;B̋lNS?X}REf/p`۷vpIHhsC>.PJY3y@' w8\;S -ށ<` l: J~y:cJbkӯ@mLbN|S>k9Qvz--JuՕ~3P ǃ}/B9!DYءaP)ߤ5Kr)m6$Jcsr_5s船9W`woڦP:E$ߠ#b tn8O{/dpl!iN( IP]y `kb -S2p~z&Y:x_O@;Sc>TM0 |_ЦƤ1_0+p|qL7)lyi Ck#1}ۦeaksrqh0 0;'b[#WBGGh ,u? >,b8&duH:ƈ6ϓI"`ͻX7 ` ?7"GR }t,s\ \@M+n 'nQPnGm߱<$L˷]-?Irj(֛7|u.tx(B/v7 ~k ( QVI?#|@5Tn7@ ^;*na `0@~+mUHіDR]o?N>rzލ*%V">l;|͹(=Q.Aq,hmSmPכMpڦߴէ r8_˱G`>hDyT_Ą̉CVߜ VP5t˰8aي4\v`7Q,ZúBGMu]:@qL>T aR[$}vS y#8lo[e\ .AhAaF67z1C&9|j`}p~r}t )|vm֛~,%Eq!?E]s`뫍76Aa*pt !0ԣ<ba5PQap)|1ЎxޕbsYq.hP⸊s|QЄ7ur$Wq=~j v9GQ 1h~T`U]K:h!g@Q㹩kpc]qʱO c@}AM?9^E:مut ;.0aCPWX7(S}`Acn1$V}^Sz+ 'suz.4}rXUAc@^7[l'|kh#8W ?McM}7^@ c%fHy\ gi{@\l@dGƉ`Es!=ޡc{97-RuĦ֦rbGYJE`Vsi9C_(_Rm .&F%IDAT^Wog _CfPņSu8qF Pyx}V{AĒci6n-ZJE^q!d+@ٟ2.⦉ͯ| ˭ .uʃa%fSz (_n( ] p hE0种ö{@SĻMiHuȱ |7v_;dwd?la>3/AC?(WYІAilۘJ>` PWAƶuqB\X%8m3ýB{guc)4y== AQ @^nr};`Mdq_cp`=$pc4EޠeMiHR;m,s(W|0tx7ڱ# Z~Ұ<(s ¥0BbsM: b>攗µ N~!cA- q^n8}qp8ohw1F+PwW;#`pz0( %Ĺ :'✶V+i۟A/nxiu} .^|FU},RЯ1"oEGeqQrEHhu^nDhF)uJB|x `;fpL84_E- uֶ oySSq}v8݃waϋ/SDZ JԹWg1տDJU  `a N{hc%6^UϰmCCz.?1G_?7dpft^3{uނ߁tpnio.mR?+@Ჸ6%Ѕ۠:iaddwqRq13ܴkzDkO{77KpL71@+>k,JN$a\FXo;D0v&ܢKC|B~S'n5Ja~^ f O5ܛu^o ߀硢ЯJo};'i?q$<u-)ўHn0/PTN7gyP\WgXu`p]XH-vAn<>Dm H1P.A>²hswyH8j[7>҆Gq:8WEžX.1X]ᳱ/4:ua6;*4߱[5>} %uAksKS `sqp)xm*y0F &VyuԀ=9`;"<y]F0XRe:= m!kμm"^~n]c̣zQ; VA>I !*H;[kіB}:<WD]\V~tr,@;*π]@(Lܠwb2c0 q \4(ȃ)|nkvƂ& S= vc,m}d9>V ).$[}zwa[եcƚEj8Neǹ8>R-lK5W-JmUC]kX4n?[joan߾Cj}TձKa%fkL[7- e@Dpzxg}BxԀ/`0>S7e9\ccyukl567QU<.1+u~c|]`8 n}RAnF;˝uCNSͪ?@0b?Tcԭ8{+NSX']Θtà1驁/[m-J }RZFDgaIM]y.d9UiϷJtc;V?iGS:+N=_<mm]b2V](b<}-r+% ñkp`\ kС1{TGKr; }l'. G0<,~ }쫎\r{h7wiN]i3{~4A3SMԎ%ռ5U=(rT(]2y^kSKz+M͗6ZcT&>i.Ϝy]B vE}SYR/m7ٲT989e8飣ŷ<0P3y`-oOp>F.S:=oNVX7XܕpxY?l6kb='G[7>OglcO'y˞4oQſK덩_Om]봮̪FvN4Rn,TB#DZ+Nj>?aZ󯥡ݦ39]{rygr894Sw#:b5Wkuq[q<񺼌NTIyv:c#xv,Jαܬa? ~ AY/rrYH"2F?S1ubM\ _aKPq EuJ؟L{\z&Nܙ6^h?3g Os]5MygÄzZϴK0ƣOwJ}8¡V$Yvga]5馩ci)Ks( 0 o+޵r _ѽ;nTb<źl^Enm3m pЙ{:6(_v0O1vg~0< n|_̆axD;W3U,t.+ 1ڐ-4.a5QʻCLRSIkWI/^pYc^3,hP':7͹p!=rNK]/o^6N!{~*K,̣o;͛x5MCذ8|\Tnfк -`&(GC@S@[|"305=7[C ZQ6x!bI)Ыnm%Hl o4Z/ne#lS}-毁>U:Aik-9s+c䢟GۂOsc" ]|o>/ T,TcɸiCҌ $ZO[tU>PuguHx7юKkO;ڣFIwAYc^ 7`qz~Axsa##_NFڇ[)̍L|%ҨkQJ%,ݒpan%&=hZ,w0#6p>5uڿrtǵi67ſ;%y_d/mPQCE[NS  <%QwU3tx:chu_ I;~0S_W\wMx.ӎkoV|,Ӣ)$Ye)vjf?{go k3Z=&ptV紐Uk;YyxX%"{5AŃ:pCIh5]<7 B LSf?c7:BGX:mΡ?L2q/KckD4oe!·)އL}ia4EG}Opo,#S7aShJwNoY]j1)}>6 Җz7MxTgr\fLuQm㻙-<+l%=^1{wbB\7E8y〲|C,r7w'SmvU5®V=F@XsPSPKhk srg·@?W=4/ (q8XԻ3sZyJE:7 xC׀c׶yg>~l ʌ\3df~a]Aje3h^P;3վ5o/XlZ'B՗S:fayw}n3?w1 kg+"ﵛKnꊍcǵw;9u< !?RΡ68^+IRj{c;tPa#!t]UYYֽ~DCQ))_;mJjsb}),f.䉶[ZO YV|o,QR-5B{cp?hpq_7fܠILGvn!ں!8_mfZӆ2^J)I!#;[: a.tNaiA\ncm)*$t׶2p<#CQ@0ac VZ[~hVj~U7u&E.z*µƏ؈yzl}u EuwnpKc{F{S BBmBg\׵+?y}rvGYOp|v9~sK]Zҹsڒψզ;|6MSgNn؍O.;.WBZJUV;=m"bA5;P>» [7G lSqb,v "YtJ8Ӣ,p>2zp9 <8aR~g-ZS.}z%v?Zx}'H B:Ĭ5'8~ԵaBbDmO&GZ`cFޠ |o#7PݴʖА}4v7АD72*2e{b;| {0Dxܨ߀h?u; Q[qg[ٯ[PSOWhr?[oE2 jz*6^hlkHcYztqt(n Ż;|H4ƹ.ݐFឥܖu m|צ(.lw;O mĺhGȳws@OW?aDjk4o9,5nb֯3~Asz#rm@hբIUϏM]n[3=iluvSuaQ^3{d:SG'uޱAL_g1jMu(Cap!:J'^_RD>y4g9i2 vG+URu/~$3᩶mƮ^9]Aܮ//V+.b)w~+AH_(x" B[1ɡ˧iO[6-ڶ6&8 n4Ǯd}΅;1xHm+i~}A*W7YuE=ٶ#ڎs;BVJONV:vB!^ï:xfY:^~]w~n k÷OUI՛oN)|P~Y6So nrT\O>[`LUgS?įk'r̳;~ !o\~ 7U4DZBbS!ޓ(Ѧ[n5m9CڊE2 @M":cMH,P067 ovk[],R[S]WSz>?N ϫ0VC@80 =niS}p\np[Ŷxfˍ ~6 6%NxT + "ow}lYNJ>2ӄexhJcoAr ͏3ڊ4mvkta 3 m;0?Ni |;N=|u٩CMu‡[]O X8-JO[b'O9݌O;Y};|lVk5 3M=R!W7p(+/77L_ :rCY9r5hla$p7wc: ;] Y}tbiTn3թqc鹕91]椫hjO9ç _fxqBE*X_r÷IGT{7&>ķ>ء.}\5_HlWp>zbFb8H@=ڨ3ĆàX[69<e7Ҝ>:ϋ_,{E<-\Q0OZқ^KsJEG@$mōx /3UԮ&`0kk*o,f CG+ʝ@+m*wd)~eCa4{Ee>)t⭾k n7@+=P96`{6 o:|t{doyӊM*} s L]tho >\Zݗ f` qy`S~P}=|)_ < >?$ۛ?g?5pkj|i-!@CTt~F?aU\jpȋ]XJy@hXV-MM|7l_|hnQTug|1}/?оa TQR-eׯRC~%4Zr xr|`Uoݿiɩy8\O݋@X`=ߏݿxz\x-2~]Z?y7_ڂot/t-"Km[ERxΩw,Fji@֛ͬ3@ 3tg9t~oo H7.!_PЖWo!/i9GƣIs]7ӷBWX^ 䫿ީI7 fVUz{Ntg0-{-e~1GiK,>CS{RhZb*['pjkR94d\Y$࿸^7 +iYn~#r,[q V9Q*hXoLjᨅ&RRͻ2>_2So|&XMTy+>nD# @hj@w<0i Ӡ|iߧZi+-7i4k|ATݦ{mg,/={hwa@õpcf[gZ5/6bMk--,+oaZ%I|^kGM}L; 5ၸP!=еѷ5jTuGLWOv(n.~=:okHy oZxnu'-K驏nPk&-r6ʬjf,7zn='u{}ź~Ԥ1mWa,@~3=i O4`VVzT?&7{*+z-z9tqpؼv3X5N+:j&u !|WM8='ፍ;<"Co:~5 f3d[4eYgMF#EDԠ_kѽڶsg&h*/gCD&Oi9=z<<`elȁ䁼_E9Nҵ#PoMVЍq@Dx7===жKg͋* D2$=pb,"z=msi>[NQ;hφ!6#t'kL==<'{=@Nd_s@Y-3C@PǁҦR@1HB2'[kH-}ͺ&܎Ums_4kG@t=O=~kR`~ש9ͺfӞQ,@OOXY@ 2v>s[Y5jYO=='nߺ kU%}C P+[܃;78OW8# 'V\6h{jgxok1=g+;lA҈\6]8#G`wynm/dm+p@'sV&3w,ɖK+REؗ}XW>`K14XӣٝZ:/Yr,@i^Ij󱟍?$ߠX>@A(FD- l,7z`G0axrmv&z -z&X@Pk٢֨[,f;x$ l7u~ߪ;+V~G==<wSce!O˄]sy ŵm16&OY u%|&|A[7٫YbkcۘpeOy@@zlZ,*z`z \wi]F{֕ Y{ǘҺ֞`f_lm5eii‚o[ȽpH{~1ce8i94蠜1;w5evQXoj2{fAQ-j/뵿h8~'voI@.| 9np({Am6־# M6آbFd+c ڛ o[hbr@x Ny1k@Y:wGf2Z=P^k[f=mv$?f>  v낹vҠ_ %ue.v}8Ml)!GN)zO&} h㶤ٞ69jݮR22 bTMꙫqBc?]6V~'mNWasîaY4.mZaVr^ u{4R!C_ms53 -n( ' }HJF吻IZL8R@@7<T7$eM8> Q-ƕ4Q%>c&02V/PƞˆF5Pke~q=RݓO bU[lBolրQOiTyx7 SKck&ZaCdެ"oNxO9cxP&AZy#؉#I==P@x {8dEVtYa>Y‰t  Lt!kmim]51ϱbGˀ)Ȏ^}137يMl=[EJmWܶvJg+pHlR n{aC]({|A<ÑGDteE== aׅiAXE3?O`N-;N 4# { G`4!`xpC(p ʸI{Zu_ ZBD545 n.W[feB/ fɮ0J|Z&)z z`#EDt:q9LItӆ:9m a/yO}z's@e}u(j/s9mUSZ-Kh־1خ n%ÄNLZ]BAv'@>B.xّGDdx5R@@u!O˄C%{8-1ѻ(0p2_-\,L}cb ;  ֱu7ӼVh=|.HFZ mC`{_q}yI>z KLHx  z }8^&2V aӼ;iI<}JS*zp9dqPy8Zg:=헄 O|e[7 L L( 4N{Q',( HL(,[TFLxhoHL(̋!GN҆:H)pqG>rȫYw,=[À8X8_x@S{Vא@ͽmw|R@pv  k&*f < q: u?J$tC9̓ G-6+?z|W, y2H1,#|D&0!`gk|ʊځ({`&o2EU]H9g%IV]QXlLF LdV-)IdےGpy:>DLhǶ A e{ސ2iBA@=]j QC=e[K^Ī!-vPwlL`"\/<#x^;%ria+x;ɄC ͒tayQ8='! <@g~ȑ}'y:8%D l_*_`uwpvY)]r)UҲ:Jy%'a(@] =n~a;{gB Ll+VJdӸLe0-H=Xn8ˏg`,w<"vml)HkS*7yVu O:*-w]u,O`帑-sw (@`{_V LO W m ua=.9eO?V>dtNS.qL^,S?p;ՄMs|D\2w9nz`t)?E~Sb;Smiy^fwb=3Hg%'}:--e5YmR'r?iev:%v̔6X*?kb9VtQ%hBʟWc{HSfZSC*,y|Gf=NՏׁS_帴6F넥Hw9v8p5oXmm+I%;MaYQˤeP{UV*_5iH됸H8^xr&qJV=i$H(nP!¬>mk_2w[]ۡD~V'  lN L2N+j62E4%iTq^g[mh^O>+]}uxjV_Ҡ@s8(ork%bGZcIdN}z'~ofY^?>iO'ViU.dPn~g4\s}C.|.~dikޱT;C}];kN[SJSxl*Jy+f?օoH̤.\6:ဘ\*+Omh#{5$$>(iq`IL(,exwd tIaax8y!'[LVtN R \x+ݖ Md u C#u "M<$ui{°>VʾC@ϤdHL& P2!8g1_ Lv^U%Qzt Iy٥PuӖy[Bi[ u6~`#obk5b+uN׬ac*'7+|oG:[9y`4,nxFCͷo?uҍz[lUefg]Gr`9VgZfOyB wMށatC^M4<_t>#ē' L*²ćq ,[5R>m}eA-!wCX3Tx1P#BjU|u:gp'SY$ {H]\W͆j߹psmv ߑO>N\g^kG7}mPջIO+zB>۶$'6kE'_6%uO,YFzGۯj9=`nPC=4hM߮.4Bj1ڋܕħyK]G\H`3TPL]U*2º8(&}BU6Ī?,%:_#tdC]l;]8]`` +rXr%s]78!C r xnbq{PXjrIV hivs;KUS6/ VZ{c(;oo SfG&}z aUOm qrqa5X8؊zacl3KYstg:pWqfotK]宸ǥi" 9+`_)k}!ꑫQkRW#Kawyy3#uErX,!pBƏz3qN\'<)Hvb#y+t:O<6ԧzxc?f]]lz%JCxD< =GT=J_,!lސѶ`R\spDE}YݏjLJp<WP- 7Ϸ|v)rg5N[{f{عaz:ޭ70iX]2>_I+@<zalStWν:/iK.r=.yg|6Jxʲ=m/nH]!?&(NK5 ڀ> {[:a-|VNuDp5#tpp}H{ ҆-l <`nhOӸU x oJS! 11D -gO E%ڀΓÞIV>8*t!g`|k3mO%ۘPW<A!9'Ba]HBN;> |sHK #֧`' \ 3g#GuZتC쪍.yXҾ~ywC6JGg7 knx bF/ gh˵h lHg/\w*rrYw iN\61Kit{K~îOsOb>\ak7V>,p'Ka8Lm&-p_/C }ʞ)s&;LAWBDDB;\NsfJ%ۿPs0Vk?h;J;g j?[rfX k0zrG?zO'l~-Nvr.zcW[.!D.fޝ=uEklIիZ I&R߿mݮwޕ  TUt- (!׫7ծ3O$ y9ADYH烝$rd a}YiЅUl{CGUNtA|Wui1r<cAY2@#J|G<+{4ߡ d{r((f`CxЦ֝Moe1hSZGldj sӱJ^]jL*~d2zml1`qجjr:]w[vx\-?i" mJ.i7v K 7&>H܁( ֣#(:?S.C`P LĊܝweSSX>¾RT`C<+Pr VFNS ?0nP߉?WJ .t^+*!]qL=M_- yhrӹ_QoMC8A}[]=@POoj=]g.ſ _^{M5 ȝ5˿z#.akT#! QxtGN2uZAN; jrsNG-abYQB!|7L BOٜbㅗ L(V?'juOSaPGۖ | O$RgU SQ; aW L8_d:H9/Jta|^CHƇqYe.rx~2 6RxzlvT8M &_r~U_kվc6cMr#5oV[{=c.]$-6cfkߝ eZ!yt. b0 pAYm,ۼ;x%fߕx>w=-6{;Ѕa{PC]()^kl_muQVlx2^˚NH)a['|.A'窱tas&2:}O|8yX͑:;_ Ă2hL>#ǯ:<&Bs^M$N!N5~+v gy8iK"sZ~:[{UI{ejе69+u҂}o&'I.üaOrʃ.' ^$vt]^CNYԙ\|Q8M)؉L4'u.{vΠ8Sng nĄTR}:Q>yCn 62VD9I]9ezxd| (i6JTLܾ0j0ۂOV LAlE ]Oc&RIRVr0LulOT- cLy=+5c_=M߶R7o qk ++N\7W6t&|Ίnge҄,[WuYi>Jzm%mIU. d]u]JiӿՈΏ ; "}9OO˩o'DN0@[#Nvv"gu8;sP{ϠF`W IOzک~}#xa=(S{L/yXv0!JGNt¾B)M>1* vu>YuoCq`~w[NvDؽ8p|kx_իA5ar 'b(@oT;C}Aa[¶RXzSLp@6zi6' v!'p94o‰\a0VO.,(H/_ºQxyK2i< OU\Wi|rG{et=f=J8m~ hl[p k[\۷ի+m_p%T0yFrQÇ2%\$U]O>Y;}%XnбM'BP]Cy|xǖa;j0>?`Pr!S12>C]-G›kn*yY]t7)S4ngx [%.lXysaˡt\Ze:d;4B `[ ԟ̲.פFdnjqGW9Pys(VDO^@IDAT -uܯv ( g"8d'"I˞VQ;ѿ ;uOHXepCX  >Pԋ ۄOC 3` YyvV!/tuN As&3YpilOiuprr~Aub;v9s8O=O5ީrmet^еߺV(r)-pG٬C;й8 #z(,,y>0Q\#գ㸮 nI.ǾQᝅstKk$ t!'.谉G©0v&/Jae56Gr]9%s[z}F9y0.q"/o]#灗%rKjqwFI*֭ļ̅$O? Wo軔MBeaP/M٧v>*T0Ԗ8z%313Knn֕֍6F|%e <<+O<#!]t %0.l۪`BNvƮQv .)'6leYJ}3K|޼)wsIz|JhO2pá}:ݎ6omRawb!!"{Zs:i<68<·`C֯Kn7z#LMgv7۵c{5[8r~[~R굒 g=f{CgF͐yOv6[ik.'|sԢN; ]Aѝ@p2 `qyʤK)vpcpE`EOj +}VZ/=ʫ(t貈tE8EX$Xv8^!ϜSuvg %|o|2V2vB[üe$^&p aE'!69 i$rȱс>9~2e?r޽@{E ӺJ]z5U7WjV5=PH]zO.ȒkS_E?V`]$-2h6B=` A8B /PVTD㔖 WCWy󲜻,(EK{| >»CUvX< o)LcV(_&@<*uw)٭xʦ. InH!t/F nȷL,p<97r+|I`2 0Shr\Siew etKi0&|-z3;u ۺYoicYzNN4M-9˜WTMq2G̲vgech]0|駯nϓs/5>e2d0C8I{tvXm:EOs9yw9{'uEy8=틅 ظBmF^fxׇ\\A C =6*[`wQ!BgW"'{8o?-aϋ.ԇqqpt o L:]~vs |=Haf.7I8#0xBW i CُQC|^|ՠmۭZ#Ƨkبx&ޖZ3&vfj lnup9ߜ/b~͌Hѡ舣CyaA{GbB/= O JO#qNڴ+ m&<6eO]G^dV?*#p-xZۃ2m,OzB?nqÞE»&0TְIۦ-yĵm joM:ŊΛmo?boM9e5ja7֏i|8v ]8-IvH+iZy,Ztt ܿt:RG/X/Й{&r]ec$dV@ lw2BurYp^NV8y~O_,$DzEHYL.73.(}~d&;'3}dvs#d, PQ'+a&cYuIPX YxfG~8a@]{Jԃu3V@`geOG o-sv*"e@{)MᓭvVOWfSI k<'XQ_^9Ӽi7<0Ugڢ/лni ̬tOPhxV'2FqkZ2@vuMөp~x+,a1ӑ0a]+( {t^͂opqx<;^#!R >Lʊ1t"|^:ށ캐#Cvb&1 ,\*KoĊn;}E]`wlǴN p<9Vd(g38pn@'Nnۆ4Ĥ{) _ \7!ypc7lVң*'!텖 ?:nta9A߲Oc{k0V'ufNaZr=x67Z^-Zlu!nxndY͛cE=9;Rl8_.[`4m%W&1;g_|l{/ZtWkغ2%TdPv#;CMu0Kp-IƠp@JXIE~NӽFzV18 a:ZxJXC Ea|]a| V=!p Om8^uǑcJJU3z%g;<)ԅ2°xץ0%B6cׄw |ȵ=I+ \_C .s&uA'O^¡ù|Wh#nj g5fq@ 5dd=1{k vڵqv^⁉&7JA'ݝTu_f=pYWm>4PW?tݦ?:adpBs]e::x:':7u; #Uy/)66ҡ\-@;kG.59$I ?*x%SJ/]M8&5Πy[犪+¿-¿ Z\(|_$hiӅG.9e,~'Q:%VwEe q.;'/Nx$E?DAv?fxZv]~:i[{fvF5V5H2q%d"=1Xwchdv>Ѷ^IC<9=CV 30p)*{{ w!eQ@;h:Dž(~,+Hh!,)+n;ݍ/yu{[)\a^7 ? L3PV}OgO8OKq:?)spNes;> `ҷP~y99*Ƴ?-L`›#y{ik5(*!p={9f4юVNǧ#qOKI YK SIn䀃 6{ju_S|o/Vټ'ő=Ry#$9퉌/~)<#x$&8^?<]"D=މ;'-=A6Dy-z!oOPF <+1E |HPT;j حkZ|&==/)B4 W.N.n8&74 jfvhC! bSBkM&Y[i#7KVW^ QY ta@ιB9]&|K"Ӳv,cYe(Bnq;[ ɪaNJv4բ6m|j?r[akkM*חKĿ{?${\P, @O-v]}'XчًrMrmsƅTRyХi;m1EB9kU_teu.6LXqppp@} V%ae/)29BA NΫw Dt7,7sY`57M>!|J`iBq{U?|@>-`3I rN9-L-'vECq%E$gΉcÖ7 \ aHrp0V8D8w_$0`k퐘ݑ Yi\Dr}q`Vap`ZмV W'\ Bq농q9۲e&Ժ D\hZ7Pth>.|+>rgp۳)k$?-5͘;t@_+\N@DsapvVվyUP{B "ҥXHGcCł**փĂH(k "z~/IvB +p3;ؾ*駶W7kƫ3sZ3/m!}2w{xRg<= AG#&?̿3<[nƹTGqp(~Yn Y ̭TكaJz¶r 탗+7m2|ǧ@YVϞ_&`IQЍ'ģ'4cX]J׋㇐<Ԏ4ըOy"F1 ~]O^xzN#m6*ƛv\T?8+Il 똨c0w1~!x Zb`.p{M8ԩ>V}ěƓ4'~89v't˞?Xz9scQU_kw@s͒Jd%LaRGceRM#$ƞ6*9}ھA KY|(ك9[%_l<3uDC4mkD_$Qp\V;FzRloݰFFy%L/w[v|8[·!BnӬqxu||>>B/_~bOI}ծ-V]ē7m=zƿ(r)?{>9fK5/5nj\CkS Z 0( VVx -[?8ܘS?jGab|XԯS: vOQ߶ IM?+a",ZLm6`O 1u$ՕZ_em{=Ao/e2Eƕx-Nc:~M1<0)}/@{mWF7cw-E`ax|, ^_:8vsmJ}UŰXIʩac|I)i1Y'13`*8KH6%|2+Gʜ19(jkr8/cKM* e| z h$T7MIXj_?}()/$Lx߻[aIěZu}c(/=5p.L+w_5^ɾ{Io##nxջ9:.Juc3f'O-g /CE< =sbަ'cP5st~\+²#뀿 E팩88vYqzsG m}?j_+ỤԼM}oʷr„^wԛzS'&mpel(l(ŷWज़nKISiSygzY;B0nz2;! j?rSQC'6{ PX <#mˁ2 /NE,_YoW)Gbi`:ݒSP5k%Oڜ[ Gb<զ0>+jG2͸vq^OǚkLJ 1ymp8Ja$mK5:7_Ï`}aQlTf\z#ɛx3txWo; >~QnlwPvj^Xx9= msGɃڑfqxL{ցk`ABE .VCiIX vt~j:~ mv]طԇ:Px3\]y#|p-fyL퉻'!yIz磰/S$uoG4׍lw]'@߯2^ogO,K~Ex!뢠X}6, |WLm?]/>4\[㞸|tۂe<| *KaunL+~<+A;Z΅k¶;2q&0p e jkt7`}rħ'oڥ6y8zp r>#RN~mvL _7^཰) Td>uZ3'~LFn ->3>RZ7y\_^N*OŒ>U2[ i*6XKӰ , }Fx ^)3)y?v|W~_&8C:MM!yFwO]Gh֭>hrH,Dʠ:v/?Z`HʬKlVX)~ `x#lp5G xuY_r+[r^;ׯyד vFp+G$Am2E0iZ:${2\mڌa8F`M=dä( |a7&@do^p3+)dzx iKC6<3U?E%Ґ>k;L0//mp(lkx1-\!p/w,u6ŶXDjcKxb3VH,KJ걩+5_ߤ۰;zڜ8Ηॹ)5 _^@{ {8JCbf%eٿAsڲOˁQL| g |VbnscݑPOےf4y-gcI8lwm<9q9kVlU6ReK/=mp'E D7k\It-:s=>Slc='au13?^6{Y||x\+kPIg49 |, {I= u_sbݮ-fZ|lPn٬Q(ix ]{kI+5km΁u;ny:|LA[>HRh|149,b@(^6WA xpa _HzڢO$Ncۛi1-}5>-r7Ȟ6A~^ U>:vh>1Ly#L`}p5u\P^1(zqO`?%4[ X|DwUW1; 8:_ˁCP|Eє'rv" <{ w ׹nys'>)Dl&p>EH .ow@CwPhZюĖaXxfl KGp=/B܄$7k5z /`/eԎ$-a8#j&f q-)k꧰?|61$u(SqŰ Xcp}'?PFM1z :dv =/:X m2s=`gb!ڑf'rC" %^v{))%X<<~~5s.̣ا6i]0yZ_x$ MV^Ƅ$6/I:\Kߵ/YQfLO]}qIc&*Ӿخh*L(k!_;QR̐ |ewݏĴ; pS s4n9#7E^^_P;aiX~o'LӖuD^/<^^`;mV G#)o44a<ЋHv (l@}y;?ˉGR}?.;Y^}f$ƺ6㎣kuMp-z߅UqtN\^lg|a~^à\y5uxnױ 4,AHV} Xz gǦs6ZiK͢>fA٘ -҃M6,~9]8|!܄o݈qC^ ڪOt_G{K6?N?Y:ROMpLn&2{3{BۤOmqC`9x$Xm;njmzBnZvnm:VIG 7u*Ƈ/<\t7q{1G!v_U<.Ǫݍ.r9xWyy|lό^d=ľ>B>0f롕C>nޤeLy8Zip;P' \IJVī굌讱lqikuk՝ag}iƫP\+}M>ܦ[2f^C펩_W ^ԯcڊ:źJVwπsp=njuE,\K՟hñ)%p3.;Ij.eꓺ~*ǃp|va"8? :[jݵ D؛>U^mt(5Oߦ'i{t`<86^^Tm8\8/Dx mddKh<0jGw~);}0l׳v_^i%e5peຝ^I=|iG>φ>f OGudc$T{"v7py=|kXfED݋g1x)y NUj+6z~k;O%0x< }( :v;{n'P y)ؖ!Dࡻ?x~ @Z>~kf<7k!yzP}Υ!s F0x:/~a|:^c}Cn7as^ԩ2^8jM֗]b;kgIZ(L%S׏e) t6VN:4.$MSR4vB5 KU#d%]!b&U7}8<tmURh!b^ o g݊iJ3ZMӿe/,wYCKc\{F|w^?w|&p<-˶9Ҍ״6gkkk>B^ |h~^JGv/z? =v1OنNK#l/ `JwcSQˬXEh{uC\ql0,bx 6}Sj_O:qpΧޖ@u<}`a¬a;nH6 -ϼ?7m 2uɧ_~AxI)׀P3_3s.|p,o%ډW}v"摏A<$e:~΋ޠC{x`S6| {̟-MҞ6k3/,X-C8V/ŇY,>{#!)D; ɸ:bK-Qvq2ιuBk8|P)~=&뉡e, : JK0g†Y;k?,}&FnF=}PhA{=xhW_=$^ ZT|Jߵym~w ^20ChT{-1b|||l!m?|| \\m;,KɾMݿyHy5_W@IDATGlzl^p}*7 ~+Uǫ7lEjGO gp- 8\ÖI_2 Hx<OԩƶOe-0?V1dS蒍QmSf(< L/ _{{Mډ{0>,_gہ_U'ifkߛ6Kd8 .ۃ]`Y^jxsL0Jꬡ)aK8:6%`còSyd'}Iֵe`݇W`WX@݇ҰyyA:[9ps/`h3M3]-l ;NN?"7}~| =|:OAƁkm]p3icVh[ h.Y^ᰂ&P6,x-M} +?G`Cͯxrjo=:R_<(? ~+~A}C6(b:xi0Vv>,Cߴu ]:B`;VMmy/ckp,(K}RL$)7cXLsN#·GO3뾺Ɓ{վלAby5q<, Gco]וXGJ⣙ךoϦp#elG 5n5&gTwC^ :K|n;McR/jGl;aGPr<~5xXɖkU˱>ˍ_'933aƬ`c X$hWRyplsjoGk8/xmv`KnW,ӆudg:0VwYyn#\;Z>N)M: |~SD팍`Qzu:P;ЖP{S,﹐;Ǔ~42u %Xn /` XSosoY j=iW#,׵Z}v*gzc(qC3GmC57`Z&IOw|59i=~kBx iݨ#5rc7ǃ湔amqԖ'}+voK<߀=%$/w:_I؍=׼bb%cZѾdLW,cmW}(S$N^0Xҏ66<݋fXE_*~+YŸR8ğ=E4g\Ʋ~>|vj_p M@9'Rۭ^7~O˾a8#2l׸5x7WW=pGRG x?/!Lk&KN\w; o]Pl Jxx;߮2^h@%~ZHp }.Qc{'kVtHmjW >|LWwXmI#KνaHO|,5)\ k Wk`EX ܏<~ c5IܵcC 8? ]GC#XװG *fu/%4#ٽ gps;qX<;y"mCJlHSe`lӝpmBmmB2Vmafm6^{X=RDG -|9f=DCU3 O(xW~=0>,߹R\wxW&m`,Oi'M'4~\oag'͓zE_!?kJpPyzΜ ]eux)BbGA{ͯ- 8*#!K;nIu57TߩeOk?zk_ƽ𮅯mjy_mMy78xwgb![G?\׮$nڴ;'؆nQ;bO?)ۜ2jZAa> -`zxd=\?T?-cS۟tPW׿&3L/m@9]u}QcZ$ug4N\@0e#%e[yct\C9WourP;2AsdaߜQ<2[ NYM[[Y,&}n SXVl_iF`lj\Ys"|sDRG6Sj!~ x^3X bZ}ԑzԶT=cz<?Q #mp xW1=$nTxHOjIi eXnp8G} }| 1P۶)Srp {v혯/z qQR iBmcc_lǡ N-is4۔xf{<+xpo^H;`y0jWq}i|CePƒu~#z3fVuy3M7~U5#U"s.xy>tn,#>cO},ȧq|Dx?yPЍWq\C -J}6Οos (icXm^8[$g(iW7id&%~c\ .= TIMP5&ߖ6fi_YˡoK3Jo'zQcCӜ̆ʜ1l٠ȍ4ۤ3ݸxѸi=^iL;<ŇRӺYwZCb(ߟ7<[׻zHZF*xѦ~9Sq@X阦_tnk/U-m?]5,a5> @1_?K೰.LIpfz2~-?c 8ea[p/ˉ?#~u^RBKI[>(t<&D_^|QssOL\ڞ)!ULlPf䐩|ƯUPuSŰL7W.4bZCډ:PjtJ<(!&LmO]>{o[|3Ω/>J-ϯ-_?!m-uZz@{n~>RuGxO-3maeXAd1Sx/^v>rQ[MPAtTb{}p(vw xo]<*Sݹ3>Sv¥#ƕk^b!*BRA~CoY  ;+p X>Yg|HꏏfJnlx3->p F :fڊlִ(i kF0rc_>GuB:ܐylkڶHx氏='i)wG ~+߃8Uyz>Qixց4(x{Zݲa6娏 |h {D߼C20x#~%^äY/ZN; #$3>g=qXTqmU?u4by5멻-=)Ωd L8 :935`8fkιxh:SKdmɇ2G`Z 4}OX} < :_Qn;aS8~ʰ ,ߋ/eica۸f8Yӵ%xv58^X d }Txߩص'u؉9߶({|n}=$e>~! ^jۖp;2,/J=8.}I I؍^R'6/uN Y Prnme1N~;'Kv%^͇!Aua~P\ww6ہy|d |y*uŸc|#yE=Kb`{x8VE>𿥰O>1uNOr<]z,]Ju8֙`Lk[ ,[_lg]C#0|`g6\U]`|lXu7pyp%l ™s'>}\<= &A=$1nvcCJOhOZlXˬu/\?gsv}DLZNOwX=1xQ?8~@3zڼ2eyo>|z1o> `̚l;zjнT~E )XCͩzw]_q~m^ An1TrSj7> S`'Xpx`U8Ɩx 룞xt|x[ 8vC!F[e8'RmZy+zSe:!Y, לx לm_)u*] iKK^<c(?(ĥc?~6͸i{[q&!qF"Lr]_M}ٖ CIK^ÔL efjPk], 7'AI]kbz!čCpww@,@96U;Yˋ>(8F]LFtx|k{X.#$-t#icܮkRǁz]3JƦ﯏ eKY SZ 8죏/2f8C /I%㣞6m*9U| _ ׂv==u%o2g{\Clc]fM:Fo\~/ )oBo w} σ_/ik4}OӃ k|(c8͉âڢirB77 nHS7c92>Oh^$U<t$ƺlMZسN_VhObx{m BD+ƵM~^/M5:^yTw72}' Xk;ΐX_M08^۽m>xp>u* v6~}maڭ޴״s^||' #5Ӗrc񰭉=*;e yƬC|>c3 a$c،7e GE;Y3Yȃs3ݹhnOn dS/x6y9'<;9P GOʱl׊~~G7qtC%a7;2VC8%C68>? q,0>b{mmIį͖yaMAgp#u)>vNS#HY i_Au Pfh-HV-JZsSz1~7ypʦMlq<Į,ՐrP;6ZJ08h˗ Xy#WCAu MS'EQWvcݿ^ ɟ˯&6^iۆc*Srߗm?>RG4beG#iKBD7tmci_zfOźc"8uSjNmJl΃ u"H;-;0C=oOOxիTi)`aaԱs68P^tg*c uӦ8vp,*ROؚa'4}(31.pXZ`z.Y1'xx(xoB{H<rWPjsW{iT{UkZM_Ms}z?+oGr'2ͳxp{GtC$n͓8jG7TGi9Df3t~ͯKe_!lkx+^|jGrKa_t5K@#q|UP \`!R4o2e&Ԯ ?b)*Ꚋ^86}y7Miƫʹ*۶؛a]W¢`뎟k״6ݰqsw=(!NԎŵ L ;EvfIm֮XaucpeRO Iߪ:·_._ NM v'^CdQ@ֹ>K]،;~xxi"˂muSzl`W8ˍ2bJjzV_>j+mh*SW{ǩO5}c7L{c mz-{ꈁC}8mp|q/ 8 !aXN{ }@my2pXV9Q_Jw 0xioKws8x7xH3שfgԳ2_YZ2*_r߶Vl*kb^ku }VM'[.\`'p?}ԗjkV̉ *,z:m.6莯_/FIz®>n$@q0EV8|WFk%ͰP)5 E]êosiobZk5ݴH/5mZ%\Gx58#@| -~ _BO}krZvkys rPQ~Eh!|O<]Qpgͬx6X  {/2\ۀc>׮Af>q늤 ?ˊW]ڍ[-E7b'`oA㟸6{MWWھ\¯y|| g-؎p:EWt@]Hӑmv͢u-ތگ%~ka"Ɓ<< IPI؍=7vC7m?<8lo-PQҗϸҴwSMjkXZԐO 8~}h;$ y v|88GM[x 3,~+gTlT] z5#bf/aҍW7׸se?mn0ϒuv}(Gyҝˇܣd>G \mDq|װښzF/Lm՝u_N8 {ǡJ ^ԭϵ|~0>@OkMbO<4F qaRȢV]xl).'Z.p'-i i63\Գ/O(+'z[Fة֦2;N={ }]D///.w" B+e-86Gcv֫AW>Juhi`_Y)j=Ֆ6P ^t(^2+0;G^ʋm /SuSx+5IO^φ_}z$ޕ{FnWad\g۟Ih{dzbي{j ӏAwsx؞vECNwsǓ6 G1Hp]( ]YYVӢ'oB7_^$a>R/tŸ͓ n[_bK֝@uC[u|ƕ:-_u%P[ƪ>V/u^agLw,fpnL9=X-?uN%e9)8')-qki~b~7FX&Zr|蹖-+})?(}PMdoگԯM3bԷϺz] <79Nרֺe'*O1~0- w K"m֖8؆Ԁi8EOX7P٬IKj%=㵄/\{+n(7D0# ӎЃoqS >>.ԉiϰg=>maU؟1gp;M5韲~r. )$LalD,O1-C<x$P{t/Wg_%<d.kx|o+wc#M? kz-_}"xO{e0AI~/Ej^jcY3rP;E {mjӫ-nPZF %ih@#Z,$_@nllLS,{>3bnNw?/-KBi،+ C|;8i ~G<^? 8\v1ke/i ~b=CX'4wT }z9^[Qk)ì~W[@h3T3#"Jԅ1LiڲYb7>.΄n߀=_dMHI [u+Gxhx!.5Ou/y2dSM^|'_) 8 QI[':lnI)isݸ5;6y#dPP:'%fc0\>bׂ32#~wQŹdLakf8%>6l mz-!_ӳtgs *xn<msL_pLS^ڈ#ڃ'^æn|(hp`#g,zc[Eش9,\O_'\%4Ry$nNmMm5X_y qIYc *ͼͶi{l cm?B_<=g{,'>5NKjf v쇇zUٽ+,#ZhCtCq~O1p'vbL^ av_+U,׹p Jmovf_>hφa)/aj:zz눶yX[[ ?&DDp \X}埶$$iyYaEXDyǀ9VRcfu윃hˬhįElmarf9wM_PO…p+ F:VדԎXu6g[BGw~-wglA9i䏞ڵ)ՖݔM k? ̓EǹR뎞5CU[smX:u14uMminJXXO$iKPiӵvyDC`}57ہ_^?6[=mY*0<3D=}5B5bgR4-ћe5qtG-~ ЅBW"7Yn ߌnhZkX*^9@샛>8<ؼW7;#5 ;= CyO4o8#8ه~!wNm F_,5ecQ?XX6 ȜԹ̘jk^!t_Wv z~\3Kz9\\k ԁ -RO@>"8?f%aWۍu״6ErĽj_[ e< J,t.Hڠ0e /*u{xxa^ AqOJ ޔ:v2O.m>^ K 6&ӧ)Eq%x.tm1~ ^G+4f?]v+v64Pnˬ}a:lX;MCGʜ.YK3z،sz+kye:\3|C}l@1[(-uViƛ'fE>fb6jmOW-ODb7X_xXєYô7|U<7J>(isy0*ua'{sh4mW :zyn8Қx>'x{Z0y-v5u$h:>x}$l_yb'< /ɋ{8Fy A͓fئxin377?@j>\N.TQj^ pA+~i2R7Ȧ$xƪW_u2ͼ5ߴ;{~ rn_ WBjZSl/ ^C{s =_,߯z^׃sIH%3s6 eeƪ̳ejs-\M`Ş=ڑ3 -׾_wNEF ^ O sb1n6ׁqrP$f9:25lƛ?vARm}?[fx /mM'P谹Gw5@Hane 1eEغPݬ炋\+pi͍q 67YۇߟحIjWO<5$_z̓h¶}𗍴5~?_6<8/@ےqD}o3Αmk UcAg%p{6c:EkP;8g/qY] AVr$47HdJnٿ)cC>8\ A^H]5TovJvc:sI86-^@Zxl,} ./A jW_SF%-a5yFs}'nή{B-ޑ "+MbzOP`^lXzқJ"<{X9gJ2Z]u3g4E]'m&!~ xT1q0w$ɧ]9=aW௄; >)>q$8h16#ɼHExzۂkZIP)B'///sS|3<:X<Y[Js.ݦp/qK¥k^u^ s޷_bR.үv䓸[I G}T}bO ~tx;ښf h[VY/MڥKNi, [o~_/N\A:Qp*'~?^nr7aә>e&QW=a5 ,s(r|cbR.AY`Ohl?9iWKW=.LbzeStWo\9| xo @A386qy?]/ 3\\9W\NO#qI1I85׍ιupئ7`յPK7nIltӟ-M\h 3m ].wW "9/mmJYq)F cT.uJܞТ?mCf.9 ŃzSpS :oFa8Gšb(0N*eZmp8dQ,<(җ@Jܞ~;IK}:z6@h%^:vKKu]+ב7`mn p-/Rt'ՏS`'$yWŶa6|m^q _ n1omׁs<rIH=P-$wݠAqoN%QUJ]GR_]pmAYe^kIcv0qMmd`v '3I7,>!>n{v3qqNj8]q+@txkItf8qi_6V+ -Ƹ>`}t1\osy/`; ؤ\[A}."YG|+֍ +V68b̻CŖNu^ a)5ismƱp#v]\ޕιs4;lPHn  `{Ÿu$xv{ڮi0Lo^gm(#֯4pSW?kyOZuʺ4HX`ĺ1}/\u9xKeѕt;8 jln״SE7o_O.qp)8yyXvXꇪNi5랰$m->aiﭰ ~+pi?a{x\A Gh0n$t| x)QUG}_̟Gm{_c4^09_M'D?@{Os@qDڍOԮ+2xUNv$vveɵaBWp ~' ʉ`mǪeَ&9OƠN^@IDATx߆nYI퇗1p}G8 'uMiURr3k9jRݔrȀL⏫ ;!.#@_]Eu՛WuqYX^I^ES'%Oүa\hP~[ꭧgNp2J7w1<Ե'27/y_I>i{4iھ64,ǁ=}8+c <(b}2>1\ k ut]D̯2vA]u|sz>6ؾp4ֵ|)ҏI[c"E0ORtw_xuRWهwrp JI_/i626I.o'-X.jD&Iv,J]u2'#E(_\̩^ilyR׌a'pUˏP׺ůMh_iSo7+7: OkqK&쓭ן -yY5ZXg믬ԾaR%AȒ3~?~J<&]+Rqa˺&<pm1Pkwы-͝J+ qw p=hga Xwu: 75j1p'qieFi}O5ĻDƽנ|{߱ lR+O|w3ȣ!dP _NvJLz!ljkd')pho&B`saIזeFpՙ^˖..Q+(]na[ Zs1E.TE]ןp܆y4'x}g=r#IU%-7c,nz~/-%V׹q<| `?;_Nc}\LR/d8\ v=//ɳp<76)!XrxxkӬ<o:ǰeFR` {߂c8tDoߤ)[`o?:]%FOeyr`@ƾO_$}Mxu7=Id| nzp1> n ɻ^~,>nqߧwm0 ` Wq!,IMupZ%O+/K#Y79`J 6I|eo>?aPJ\2le~OXk8 #̣b]9HL:6ZkYDp8#΍L[c"ؗ>m뀗u&yDӰm\%U}_{QW數vߴ3pw;`رrO$}|RτGڐr@k@K\o8jp5l uگڙE<2a T{]s!.l xk1>X|oE¸ ~1"c`.§†-?Nn)@LM?4k]".ag!i;?7er?n7e5\c22 g}>-I9W6E8w5 wKo K)K[O l) es!mx6 J .2C\mZgXe:EFk]Lo/׸qU=}),#f6/mK?IG᣹@}0ɴ6\>߄{@;' Zps΁[cOHH's*% xk14<(] }ۃQl{' `]L+2/m#5.[`nAS3:[#2nlOv:6K* 7QDZS_+X@OȪt]79?G}cO?5Ab}WK}m\|+'uI΄M[^ u7? XG/'%lU.o'vm+㒶d}_=x.g8otI@ʋ?qMĮE?mh m.0LC,3eOA#K\wr]d6Ĺ`.b&Lō‹&lPVIcupTKWx{t7 al/ula?̡Pky^2.wnN?zr^//r|8p cH.ݵtMY`I` [W;qo)!ax1pZo(釞vqeMS'[wP6ՑRg^g}+܏2HeJѯ$>f6?2Fc޶5K}~%=z ^bV4)7nlڹx0o9Ao^Ҭ'A\Y̡`Zإ?P7K)m2NKx{䤉M֮$/΂5;A^l}x5N:O\2Q!vh'e7l{$N]D ۲kpLxKnI픸k_ 0>O&f% ǃ ˶ eu4OGOnT2nږ& 1`p$(Nz/P ̦c1|?'UiJxɣp%oG I:SGnj6؃W!{^\R? fksً2T)|הkxPz/~\cvUni?{I+|K7~m׍?iU`@ ˤo:MP-^ݨb4I9(ֻӠDo 5_$i nRN 2?u¥rd1wZD^3j6N$ l ];lo⓾Oʴ.&$0 ^tqt"|#P7)oԵ'>w qC)4 p^2ct-N΂_ƀR/~] TП8^k t? c͞MW;}Vx3lπo^nw- ƒ%:Xf Ua66V\t/IwvQC$aevLooJ3Q#uҹqK]9`̊<.] ܃$zpͲ GUǙ[ b;[K[QuŬ ,`7 C7a0Dz/ @ԋm[JM  ^>[ډmvҌkڙWmtI-mGgۯ_0I|3l7-QnO']9_'/cp<0/皇O}Z~)3Υsr߀צl9"<LhԿS{<p2pdlG))'qE[),$%<K^aٿ}_MNy c2>ibGtz-`ҶSNDrx8 +@[KYμW ƹ#K7zmJ^^VٿDi; y[S,W)ҟ8١Ͷ9XHԍ路ӧ͸24M2-៾[Kpjk_t8zf m[z8`M*iNDO<,i Q#kl/^W[s ݕwp;v*xP%w{柴{ u Itڊk`%4v~l 7:~~Q{yv~Mh(gX2x&(=2΁E%a2X7"Ԯ|SJӦ D{16v~7/?)#.Zgq3vu#J?R*gGM|N3lYG"s]0޴3N{@),Z蛮99] %:]s* p*s`Sg(J9JlK(yy ߅CgM'݇>a+e<@ġŰu/|s7ЗXnS5q2瀗m?+g䑽FŤC5hI_n2.a˱W?޺ G퓿~hN.i?b4bTYtrO'Gap&I)\NN76.qW|.9N/h|_mT^iƓ$ҵ,7 7HeEp kZ lS~ b悛oqlsw!_ȨMOm\?9?ĹC\ĀsUwAD[ M8UǹViۿm7.4څ-ù)X^_ { 8SWSkgƼexrl ?='l(8]^ʵIʊf:S,= ~ j#vQ1MY_MnGLN*JYuo9(}i|F9׆?74` 'MޖD5O /#e`Ά`O=WnHZq%>4q(N2CFe|nFͿY`pO@\˲KKw:CTm_ Y!oz}"Ss%N\H>fm.}V+˦imn.Et[~Mv<Ʀ WLGjRK*`|9 䉷2Vן5LkyQ9Y)0miCJ̸ザV]O\㖹Xё":&gStn&>a]%POuNf_9B\#qqpt'lͲ\<p+lL .{ +OH<ˌb.ue{ b c#lܸh=`6rԋ%kYZ94Rrs#8qp祭6먮0fN9\.$M᦬bŖrw\8x` Ge/`Fnx: ۃ!2?_WVgNi% _ % ~Яt/S#Au\٩g:pN7ilSNϺp*l.X'qĸϤ; ѾL6`\^+z(U&1mHXݒJ3viuӲ+hie0EWI`Yqlՠźږ፭iv0uR:A>x(j_'{V~R ^#p= .{-mmKWt}peX'ÑYPqz.vP? [ \ϖ^.7{6iBhǴwlՅ_+@:LZu"՘1]UWwwյ`^5Z0?g[12g6cg28fi*v΂@}mv2>f}ʰ~'Ńpx3ЊXvNwA[S.OXw8z=bPڮ}&+Lu pust̶h} j2l $=777*x\$st' OH:'^t+J_KW[ӯm|2_6Ijm 􅅩s/Ͷ8uK\7(xLRϜKp 샴/.^R:4Y9ovko@ҥMPRی[-?ꚸB=nB5v<.t[8*η6\6RLd՘8UwXUU'Y]@7ʋ }R8f|5jKVcmOT8|s2I=ED3l~BD3ޅaMi.g[,|\`>ZuS,f X]%\%PszH_'T[6- |ݧ K,,qf4%k pwEւ`QhJW[{k*$p^wX园-=FueʵQU{îϰmƹfg{}_+ҧ*FW鼴~pnp5XkᑵZՅoc0jժ)+UE` 5MwwW5w<@}y eǨՕmŏ-;nH2/>P5sj)GEͫjMɇ`^9b1o~u!qa$?aJ*txclaa5CpN2wjԗap FQoMcxJ Ep+uGݚ`>rqA}dyLY΂m 8PLהt Խ,yqQ˅FatΣv!mo:r2\,];%x)[)/Iű˜_|pfP΅aci.Z݃՛up([Ծ}5l.?6{O*BG3}\gOX P0OS1&Uc~#`ݪx{j72]4i &B3.^xn|vjX\ujoy .SB)a!I&a4a~ѱB=o믞%yidq}4;'o;4- әp0LRσ2zi8/j.ȓ[,H68Q}x$ə6a[?.FےO 4p,l6IwI[愧aank}f^ ?uWɒyf޺o;k[.NJ1x`g#PI]q-yqR8~6mb]~(>a6{@/jqjq5~ ,$)Oe;t};Zަ׾ ~^D9WQknj^rZg;#';mx߰3:We cޯ꺢*c=y?^>a:`܂j [ %lS(''~iwkV>Z0k{jdh~>T}{_f̸yկW^Au:

V~؇6ڥoȮqՋsxZj̦UctO9VGyC.3c843q,U'm4&Mv{Y՟˿MC`^n/;e=/u8u\wFYg_z}֔z(P2e˕aE7~ְp<\eߚ ~h|b0unƹxKڟ XuT7$6*m[\#эv U]ms^W| |ԓ6XnxZOѰ/(.2jPbM1xo[1>{sxo g9Oa.A!~ϯ}^XG GO]7 6#suW'jqMϝM"4`Wf0c39:sg ^o|?3o_y)[WBPO!zxVf'a>71RupuG EL|~RLtT﻽+eҞ77R+q[IZP{8,p!ho-s0 L7 @X֟,hYD36/!m_;qވJ\ۥ֯+LOõVpdga| ^ `om}ҫ.a;p2oPǽC8"å[m/ʡ`nxȭ^ 1szQk^h;6l_汤s{7sav}'Mu}Ws@nׯbRa{0a06ɧ[ϸ;ypU׺x~w5՘8^A?$g=/jʌ<.qWV=]?u4 p;dfUk~;lN57἟.{aLGC"inߌ;ި,\@ͺio_v$3nnWqj< Xf;qpsڧX*M-/O-pַ~;?\&@ \ۛ<-?۹+v^ > l7Cְ]t`_g_6 I[8ṃS}\s<˷؅sn~9l]=g5s?jnueδi3\.y~)y oׯڜدYo ءC&]<.r#ݻ gϫ&u}f-iļ1AΫƯ q+W]\YgF:L]`5(!;Ag*nNa78.@=sk竵,xp3pr9_SG~fI|42}o;:Q~ꊇOxkc[5͆U+K31Яeh鸯^|*3<ҩv uCDqKs,˰2 t=\? B9˹̽aepWYb~u,/OKyh?zφ/oZ]n#x8u}#az7=堪ԛx~jVZdݫWdt>X]C'pi>z`_1s5~_bӃpRiO?!-y߽~^o[s~~|zwH IFteӾ_ auOc79-\sIp_| (p n7S/tgy槫4mE]z2*#-ۻAdx `Z[`6]َ鼔q']K3,2 *ǯwkྡn:yM @P&j_oيll#mKnv:V Xx*<e Lσ˰)x1o?U8w_zO۹Zmmci6*5δ&M8b~9T];8[ɨN{OAtS),*vs y=kN5K$J@x+$g2ojň'L~asTO7~cRWz,  }/N0rqH߅68`aR`?33_"&IV >} x31x,vobY"Yc_&p7,vr폲O:H*}ٮiqCi[Sfǩ[pMS`ڙT;vӅKvuphh=S׾7;9?}Ώ©2IEdb5fƎ+M~8?jq`y`iσu.ffUc) 81f_Di Ղ# =4[QW(Ϟ]U/4oj05h.щMLsxug0xq99ۉ& @:Yha?p#h!8 A8C=OyqQ~1*o=^ R˕Z%:Fxĸ0Iڼ92p8]/ ?{ɠuO5)p%8O;_VtOM)Ҷm6+o9p | b~A;e>cV_ZeUSZﰍyյ=g䗕x L{ GůՃv[wdH&]Es捩j=1vfgnpe8iJ57VZq qciw>Qx} VPo׏[cI}ݩ >tvº|+b?k ڍ7(oa2+7/Z9po#ڍw[kMfԥp2tS˱6(v[h_ig]/ꏬ|tB[T=' <);tMKHHY.e*֥ԭ /QD|G߃[Bs/.8VY;:*,m^7mLy/_ߞ0tc1 fcTuY|#Fy-u;ݗV\sz#~r> xo^%@N,_lEkI&Kß<,7)΀`C{*oپzaũTHyIlmv^ "ӽ q8l9;Ӯ1m^>)΋خS[4NYAq"ݘ`:x։;h]8|2 Cc0 6m=~ovox#Xe.KY;-3ۘ>˚q LR5oku7|~l6G#E|5W5 &ͫ~>Su]v pe>w+VQ=cYQ9*'oM9V[yA_zw8aI>+ /mjf_q.Kˋ:m';7r7^V>p̔2;~:g4O'3 欴kC;]?G@O_^7VYxsp#Zb`׬Ŷ{PӪ:m -Q)\L[vcVO!0H)8&g;2z#M\k1k˺cBׂ-&m_:Z@!ǰ*򞪋{ HVn4O&9W]>cЛ0ܴ$S&Z\E-aRvKPrWn|"z30z){~Vc'ӥ~.1YkxXk_\izz8)lɲl.8F I_f { UUz3_ Sq2$dZ <&b F2|,pzF7|Fm8₶,ژt*o"K0N1#t ʰ389_zrYz?mӫ, ?p&ΛRmnKd8$W`AoXF\Ux_tTG]I-na#xѶŽIǮjMr2%Beb6gi7 ŚԻS!̆_|^?#} h6Iw<GR3HlWB;Z\ܺ{Y@} րٗ۶y;P#8gMtۂ^IYtZa{gnmw߮DyZzg8ؕqi [޼Mv!v=3qQ;h0w3mLTo}tPg џ-bk2Z|U#ܑƏ_iأ׍N 7u[׼ c;Zĺn 1ߦb`p8TU9||MEu{$D'J=SN%CNˁރx%d>, [3[a`߹Q{;LIst {Kۺ^]y^?t|nHQu|.uZFkrEtM. c0:t[oLegWSĶ)>HzRϘÕ\z{'nt|7m櫋}ɏ{x؜4)nMMYMZצN[gLa鯬۰5Bl9p&%`]x)D~ yHa>^ܠ7ɚ{/ {yq^C)$p:q}wJ7>S{ɭ#y2p1L˰y?6p2/!?K{{~c{~=NR䓰N\]U֭&MiA^73ڧI5o Ix5oџ['_{~^{r9p^捵IEzo6ET uXR1^ 6?? d6=7ai)Dcީ>C]XޤIƯmei̭p-"םBBlg 2ӝ`_p0dl߄߾,!xP$w8^ Y4G:ave;3t}7ǂoˉ[]]_1+Z>lKؽ]&u9|4zv9PEL.z쩭22"M:Nm@1ts #lEr|(5/מD'I; C΋'f#Xt}շ6nt&6ťnSuyѻI2?]״t{^vb>ɫeNv7m=6N3t)5p-Թo41Q=:yj_N̓Yu{SFcFIlVwWch2v7el]?\3@1]׾ CVR/%T4MN+]9 JS~G"m:V%gӾZΰGC}ӥk*׀ZwbY(cr&I/my|n#E7@j~5smys~oy*n}EW?a^.ᔡ8JVo'M V| |_GtHįoiELjE,Uv2T M#[J_AhګOV7njb_-o72-ްHi<W⬃O'O. _m$8\ܬ)S72>r3+ͯ`naݽui(Rsqq%spy#ۨy} {t>?8[eYFM9}‹ZCbkֿl_+]za\?zm#!aF(N(j0x0߄^4mc[ms֜ Z\i=Ѧ,LLҵ弈?6nqJ'`.Ӕ69Ai^^L|i'P> q9Nw&eڦMw(@IDATnI]Oo}Ƭs;zkh.*MM%sO2uKn^F|%d$NnLñ1?:W(͸2'V.N_-NW+F~ܩ1|e-tWF_@%b?VDBuLtJܞ?Oڄ4Y(p)M۾ǯ^5ӌ;7.{67OwAxKگfU!Ltyn vL&rk2Oy|޼j.L cl)TN_J u < Uo㔸߼JqK[R~\oW7 ʃ~篛+î-|7nb_t:2xU,w{CڊwS)isz*OA7EIy׶MyeX.Ƕ;gl] 7r q7ಜA kIWEt{ ^$,읍G7|!>:h;乤6\Ƹc#p>O\ b^y.ze}xi]Zvοe>^_?7mtZDܹ%񗙪ԙκEz.oWGbc'WݳpLV?74cʔ#[8] nY39_ȸc֝Wͭ Ow?E8pyHމ.Wk?7%=ߌk:\zNezG\C3Kr5x&x9h]\{Q/ /](кE}|DKt%\cͺf>nlF6¯ 閮CwnR_fg]j 7n` ^&B*(t'5slZWeJ_]l6f|3m죏[ԗ=o nRuΨy?N;鬗<nuĿ|䅠XZ>;ǸJ3gK{ W>dԻt;amE/uIr h|`c}6'KT$W1e2˰mZč]o]te'ؚǺv\?guOУM9 mO;&l0(Y0 ꫼ NژY:T!ll]mɋ׬XMx4T{}.Ze0.IΨK}'b!kOǨ'[v.J1_%nOg+j-ثǴ~:2fǩpӼW|tEQ.]]קv2&*ãIs>|3O^NfC.k}s`PwR0@(AtCm MuoOoKYk[o?HE ٥13ΟWwj 6깡7j%_Oo{ϱt4hF\ᩜIeApzT]S遛./>6ͤNC?оU=tIK|w>%dA5mƤme6J;]O~^))u I3JIpqF& E5ʹOW}KoYHٶeQ▙r&hhK:s>J94[68\2_OG~כg"{sr^hk iJ]^.z]1?J)*[}6y5ڼ;{`#+[wXͷk۹!~Dgv5_fT8l 6WV-C@`pO oO+I`F?`33y[ b㾶Ӂi8.}b7o1WZA{ ո_M#,MIqW͟];çP'lp/@<_E]IpŠbNl=Ef4c2uc⬧u+/ʹa6f<:ŗmCAp~xkþ ~ډyq$7VB}6g7vH}- aqV΄ k_ϜoyK9~ C0A樗S ʣp#~dƕwݸ̀JܞJt-p#A|4x[OIUD|.y7< ?!sz(鷞POԾq/POŝp|7+Ebjէo:_pݽq'n!@#+olFJq1x޽utng1לZ-jj¼ա+έyjlÉvYoef:b`>rw\'uS wbmF~h p-\mWқqM`~.B/oՠM6]!XD,#Rc5{+߃2 nOAp=y6M~'g?O-`{)ۼmRi8׸f9C`rk/m!(|lkJ]'ף>>L8Vye^-:{\Wσ`_ ۵®%J)̷kutqQ^˹_kӔz(i{wMIm_׿E{T(߇=2VyjJkU+wV.ԝw6%b8ZZwY0p) ]鳺vbKjgT[q*9WHN\0:KO'}b#=fh|.}mEx!l'p$d̤M>xԥN_;Ytg86'!6z֯4ӕƻ .f|.LIo-.Mp4?ҍ)7g>Ä@}-!% F z/aV+g;>Nv mZYߐO[Û>灮q ˫GIդϨ>̟=/m͙H;_N0qջſ  l_5w6>ª/#+1}>$Vp1$]#N$'@NgLU<\*N+ N`+YΪj't){KKo!$QM)( R V( Ht"ws9d ̬gv_3CXޜ_zn/H|z[bZz^kk4J|Dvӯ%Y}i=+AiVWJ|yCh3NPՑz cS%SJ?&vk5`bWOHl`py9}ܟ'P_ԕ2 A]_UrYϘZk;zwc#eqv˴&cq\?>E_]>b0= =K@f1WBdf1#@ȥG6~ q|383O;3#=~E;Ƌck +wYn o7x'51 qr?Y|j_ewɗ t:\ ρfñ&oԙ!Y&({ԑKqs$cpc`>ee~|d`nիIM1VU-L6[<4MK_P;>^p6J{~ /-/4ݮI?}WӲ|֜3V ~1?\xXWVRo{^;GzjVW0zWJ:饞: C o=X=G_u!cg> _1b]?cp7A4LV㉇9<(vOGrsï=hƒ|R_//ԃêޒ5^4 ?+O~iC%ax1l"lCl zƁ~1]x]ҧJaWO ܠWrppݚ1xPn>8ֻ ׺SMsKBm &Hִ4s|gv`W²nDlA?)2ƅ%e_Tj"aZInA`?ǥ>;_~@'^_ r}?s6x :SڥcM]-yJ[7a48oABG3gҰ?p Wm{y('1>Zv} 5<טe9|qn1;mЂTxOoWnw >>?^l<9AOdAd~>)c8Cc?\K?wƲb:T1fg?i?w{_x. dsMy!8]k3x{(ruQ/.4/A7QY<@a 'o]u) W) kOZtC)/3s+RS%O&OкLYoڔvalez]7W螇5Wۖpw֡?S7$?cK<qh3̍iߘ1s~Q"-AoQteMwB%9Vo ԶwoHc6~B J7weo ?l[/^iWȿ,F0/! R[fy?`z2-an]^P.GTpaj_\), .ELjuCx`]}2uxlO0y _Cs[pd\%nR֥z0?o/Ԗu(o 8dN؎w}ۏ)>_J=1)/uitnu{{< ˂gyf>,O[<(uw S.ƕOZvS>uꧡ8<8fӰ+tN [YXByOǓ'ԝ A+; {?񨟺WN[ƠMU*X q+6yQyy.: kŧw&Wݝ$vjQCA6ΛkH6V}~,x)bPg_ 47!/A8#o'ct?ؘG8TҼ l_Ao~Zu ]sZv뵾?q/}˩` _~X&L-z]Jw7R:]ezR.y =67OKhHL>~*<"Yw\O D j!< 6u<rO&j`Y%az˰W׍{||ˠx> ;9`~4mqXۇU\*>%/CeZ^SL[}0}:} ^{GԎx*{N pBE҇''17ןw-/qp幸̍AkiV;"};m&ze'nDjX/xGt3xo?r㷚>K'gvx إ'/_||y, 4<C 7#y[r5/5U3:޾EcoBo8z{>Zj/bz d?'5_;0얒+ֽWem>ڇWCpxYq,̈́Fʴa<z u^/vǮȗqꃴ2NۼwQX`}( }WOE|d 'L\\:5Г"|V6뒼#pfP;6`X+}̃mYO:M{KYc) c=խIbh'EԑxOaf쯗!9E.h󙧔cKв |Xx:i. /iY)1vxcfh& #`EY_h6hܔYH]Ts&1᧗`0ōC< w½"yR_ڮdiIZ>(1Xn+ei, l ΃yE 9l'mZh8;`g uԖ[Lef\JW~X^\#aGbOR£X~}(uc2/S3]{_4R߱ڷ0vlnߎ \?,ol[4,ٌ`ڪ%,mU>!."qf}~aoRo21}/=zD=ЇÍQǘ<-=f~l,,R"|.RR_7t Ӟjae׹My۵\Mh&NU-z}T6Pۜ0 VEAo[ WDێ|ܒzPڵnٳÇ %``?Mx i]flk}55Z%w~NWx׻46? ů"K}F7҉]F+}ޞVci5Vfv踯'y n97Lv.wUf~]ê۫ >ƿ \iدHǖ0}(C2?l!|zzj䶒N{²z/_ {[82>k ,e1\  sg_ҟ, x+U>~ xAgݔke&+ψC;X09>tS4lG- {9+h@vC/tu*CGR7~&LZa\ݽT텈n<Ӯe<1ƠmN(=\L\JfʡNi<V}C(.j|7N;m[.y;i r4r㫑^Vni~#rlՒoa+p~_]] P:*IF]t?=>B뷜 p( IcHn[06+I-1 G4|t}|XM\(A)WeKD\3Np.XwzM+iW{ #㽠ORoo˚1ٶڻbCq|UŰsQσZ?qxc,?]SJԖ bu8~0a;p۰ZIP&oin\av[\`e`xZe'Ap oQ8=Or%3qCm/}6ؾyp߹oܮ>̝t{͗m,5r|+@G߾㒶wZ'2'I+4p>cZ/ IϱW%SebB6|ρ_¬pi֓/G=tï,p-I…9T7~w3Bz6Iz 'i+i+=~s )!oʧ2;r .¥C?diǰԉIl+T̹X'uE"dR7E N8eƆz]_s8 $8 T0픡}H6ump .Vt6N!Z'+):?Q^>t )/}yݟ`\7w(0=m'鋔u؟G~-?8հ(pJvli ; &d~ WKLV²v2n,R7 …y~$g+/A=zPǓ$Otey{? $<\_/U' gA'i۱*.jvҟR7-vRR6м->j%o)Zƕk :| |ӟ:Ú0$jzŪ,J G?'e|ד+JB u%i^}Gb}~n^RnRS["[KtP m;ueHr7I=a w?iY鰦N:o3|/#m;YLd,T)>`샾O9G}u⤸ӗv2=]Sԝ_S%p{\Chzvf_]ɣx7ԅ+8nQ^ɼ/e1Qe #W`fio,|AI]M~{k@ƪMA]ԖΖ=V=nGػb~ <pGNvRkrMq$xy=e~-I[-C-~l1\`~kK+}asz煑|] <j%cmY;8|?gf1"(u~wol8\^nG 3#mat.$7.B-n'jx ѩp I`u70πR&n w( w< ^}NէM)`z@tu$Db_C>8A/Sdu32LaxHʔi}=i^{Jۼu2 )OZ֗a'r^^אcV2m}`8|뗙@yol{n Ru|)$xqYƳ} ,t?c|+<d:D{zY~>RƨKř'ψJ&.Xb$/ $M,3 ?` 0ݱ2NKB*I?ƞP'/A0t+e*46#cdQ+IF'1Ξq-PSԵMgMmςq ]n r8HpqJʣD ״*QƕUx nV^~c`/> Eqg38~ nM`4xXW)nrӖ%OEuXN1 x'{%#°1@)ɺmRf^}=<(阦H:}42? Ӿ2bO2 eV\ ׂy8| ɭ>4?3oN/A9ueS]U$?l C`iRxn j+< [z>ke{D+і4MUP-}^BzږuؗѰxny<goڦZ)'xjsB`K]?0^QP慯q ^S%.ܼEie M ou w1yG| 8F/U YxDҶ6=aMcZ_F'%l&ƞ>x(jC+yP[أ'nHisӑvk)u(c6V&F&?fY&c0e2xlAfC7k5GmkO[u=eP?˖*Dvc{e7aIp_o*w'q:Xe[vc/QeUoYN[]ʲ%/gڇ@q[quadkb/7 hEd cpx)$b,.ӇzQ- =l׋_IM{qap(}{x=7MWy'}n/]Jlj%/˪m]9o3}0n^۹K>s*7p|)Ӯ}[,w7)b½pT.oŸwe>?3?wEZu;)9 SlS)m~<faf~hԵEJ'[ʕ>N(8l}KhwxXG{{5ѰFp9ű+%3^bX&R^[+M)ZlI8|zp8SqS[_.*m.|7 p 0.n \_6:@8YG4"i3zm+kme KݼŮ-}KZ=ozRP,E]q{ >Cfcpؖv)%k>y u9O,%au8?}ԋZ6F ROZi+> \m&jjtLcW` $P+btSד vF=y =_MQǁy=X ?Xp?feSWʱwY,ӣÔ} kzOĭMlFS?, [6)ڦρc8\(1Ⅶ_ݗ`ݟ˜f~B4} | ΁ g@@R7[bH?aHfAj]-<;`%/d )L];uh=6ھʆ}v#LY\ j%=K z~lz'naiX΃' x;6O۷a]b+DÓWl΋YU<dcrEOkw$?/a) :.b"Y^j̍iI=JƑrz<\0a7دSOeCzlB`?2~:`qvnaD7b>j%NY?sҭoo0{9 -R]_>|T(I'Ԯ$^&=K Pe#^'g'$qLvy6|g`6P,s#`5/J׈zHR$0so6$aҮ?]xlsi3aplq/ʿ!e+q hz\0zy@r̷!iVM=!ʺ$x'ݴzb3UdLϋq׋k/'sBLsе#UǤ4s')ׯ 73y }/ھy?P@=NU`=8l3aQ1D+IzyeY-yS&{ѽOaaPs+eQrӦxp5.1'6_nv/:z ӻ~/7Dkozp/`{xJBKp*|?y)egڮ̀p&>ډm m&z@l~&EAj[ π%.}˽+wUgI)iBp7=z]oxFӲ 7/|^GR/|ցR,7 @{ֱطgz=6'?U?J 6c[A Q': ux Hgdq#x.կU ϲ]`I:NuO=8n`Ec ůJw{:8Yp Gc#;=Ҳ|/x@mg;es)auOi{/RvSU5ckƖK1 Kt]}jOkh?p}/J2 5FO6J^VanXSr⾺懕< .jI|kmEOhoiI7M- #Vt|{pN_zuΒŨ!&\<4& .VO[(ä[6GO<_ W ŭ\[x0- K6 cOHR7W<fH[]lOko(`gbr2ͣX\~'1p}}I;>G:p&\Y;W;q]fʺ32in3tJۺp0'k=8G:0 H'ʅ6X ŋ훠׀|):AfIFAMz˰n/7cs#z*?g%𓳛s$خp, CvTø+zf#Cۺv9 bc]Ե6ǃiڦ$q6ScVmk788\ Ax> ^JrYW W-wN%Sa7ђ~߂mXZI3#`fCm1 H'dquJw@!̡>p'r7s%pSj[_@c{n /Q3@= Jod T7//8N5|Z}uWf60x ҿJ+ ?Xu[ -P[z/m p}&:Qb;B#ɺJUS_\u[⦫-ہ}(874C/בsA*-龰~j0׺؞g\Hp>|5/pk֮ضbh2%ݰ=o Ŷf'jKl.+*X~s~s oh7}b"dPp? mx pqV lo&;I|1Gru[8bK2B׍kȟQn}`}>NA“aiX~ >_w*؎HVآ'LRr5nԵ/IzhH'n[L\Λ2>]i|܌k EO>E==2oiOYC+3r#^ WkF:36uk+{3`SX/Dze^z>%x 3`X,ӓKVr{eY ϾI^C:8K=ǻ-lp*At@\YY艻`σ u_!eP+4Uy̗cxNW[]<<쫒2;aчu=@ iSn>f{}f` nUi]৚W 7j~~[|fa|}FI=YN[;1O$o0^ϗ<} o})&oviimʘe\]J&=*yJ{^׸b5-П^;{׽6oеp}/sb쾙(qϋ{ZbOJOw_,i0Ѱ1OلֆC9'bSK `3tJl׽7q3Un†OmcKW^9@x /evyBvP!``QJ6xt7bq:x9z=)׉/V~\D ]u%"| /6E G8d`e= DIۆ?*^mA?~%Xe_e?//P.׫6]"^/vZ)Qb?g2IGmI>[Vf}Svm/lV7G(^Uu@&NƀL܄JY~#@.|uIhztC0vKO,m7pC- 兑|+1ns煭!/_7imp \JѿՐǀ_qz(IOp< :SyYkOS3 yݹꃉbͅ},2.WwDo&_Ӂ{a0c&ڒ/c7|$ƣa~=ee Ӷy"r~6`}cµk ݽ Xf? Z X_m'}).8+lJgu˹Nl+u^B[<} b hwOE _|Y|؞>w~qd ѰBצ+a0\[$.P[C[IJsƏpzYfv c w`1)OHӀLt1+_w\YL:VME_ڣ1MK =p^16S%ƭ xݸ "x `ԝSKҞiD2-z?|<\ej+ {oΰx? n=$kݯ0l7y Nq?oK2z{OvOʸr}ȝ]² 3*.~7pP_ŵ ӝCz3! `>), \='lTi] ݾ seo%|>z @6Iuw{a<g:.x)L+cӮ^gv<`fb2Jl~%p/ئ -v]Ҿy[b)M#V)7<oaxF8mlw:O mQ/SG$$z<}Hq%ǦWRol_KmGbX\ƀ߲6τ_u$(ι%R[=4c櫗~ۼ}Rj;k]7fv 96p$bub^۱\@I@}&/P7gMxlnTm˃߼圸سPҳa,]Lp5X(=DSnR'[bސ[KNkʴSķ +:A(PiVO0yzgƠВE@yoNcR,_Q~Lp-C7G5?M\#`a!,K{XybO~v6vyS]h9?zzia81qÉQڢKX0;Dt#l ~_n){CJUBGZRl'6˨7~8+Svw=l炬2,m)ݸ}T2u{Wq˼)д_m9MZBLݤ;L}H,^Gj~%lwa dl{ћS`YFJI;mRtS:APqUHm2z= KAzƐuex.:7eҟȂ:IlC/Y`fH0/)7i;e<:HSh}r]? .>}bt*2q/篁]/{4xl_Q: bэz>*:7I a꽌'+-bEOa|x GA?67!lI-k^[FѦ8']XAڐ kL1΁(SP#S(z08< yAw%4A[ː*]vccvI𱦞|D[e=I*jz07|iIUE1/*7l]| /ۉ^)ڭ?+ bz1^P2٨ 1UR>a>u<՝Mp/B+h_$>|Ÿ́J;6iVƣaiht?u:HʡVRn2|^JuSUڵ%{3ˇބD(D{g(a> 6e,xҗя}Q'I'wn`6ܳqNŰ')]{"XpNȤz`ٓS%A" =L O(]!& ڢH[zcOXqڧK)p OzHһ"髕G/Kn50OPsc+!>gAI2N]~%jKRW%!GOZ:]KF^޼)Siݰ.^r O {}xwmJckLSǰб;خmcn>n >FA5qX6zPH1 ͣ$sH>ۀ9,qL7}?~H?z ԏUNU>uyi>t)u.%rpw#Gz!e-͑8IĞKǓfY۰_GA' {`w>4Cie*p+<t:0@ [\VneӓݴLn^걣v2iu{BE7z=}!Kqx!soXGee_;|-8x0nC 8~ %^)H~J&4v[0}Hzlg?bsп#$4O0c=PP^@ z E'CCY|;Yܠ^&N^@n^/Nv>$ny)%_OlO9쏇ȷzN} =O3ϡθs/6O[VdrNS??d<2!?7LYLrɗ7z؛<)c;p([w3ebofM^BKG*0ֆV0z3<̙ 1lQ&|]ǎˋu&.V}YF#6/av׃yhK?wxyMFT\檌Y/Bg`=P9w7 Zw#(X[.~\c[} X"q趕xl)mzSc\ٚ=(e]]̜%L3v8~_Fp8<'=igS%e۱ d[v>Ca0Wp*~D+hʹnlk  ZkZ}@Cd&*<ͪݜ7ßvgCo_˞l2붍2?A)x.@Soq*CFʾ/y =*m ߂aMrXΌ蓿/a3u=- ^}H}.˶Q|eMtDŽNm]&%W$^9d%{,nZ}^+!b pdx g'ƽl΀Syrl‚.m$i ?U8ු3 Αr,LdN&i-z7g6ɋ0~9| ևEc%nzx9Ҵ~1uji:`$xpW?F;$WbH?vCw3xp&p;hS΃a^xEmI;/p68ڃ,dF m+y>J e"O:֫xY5u`ia&!tsx= [m;̮wKҎ)7~n'@ e]}/,_%J,k=87Up0, kL@6iZ4[¤qf߂_5^2nvKN)7v]Ob4sx8 Ӛ1i<Ƃ%0=\S[;I]W wB2gD{W E=秦=`IM1~h?zʾ*C_zho 'N{-]ʘ?K)czA8z;O/uC};l8V/sE{o Dc+";bJ®X?׋ͬ\響758wk94p>z'5L%O?;CoR`A/Ojtj+'c@&\,f~ln:7dzPƉ6pJ< =,d$~մ^=}m+jXC -p8@_?^>p|(hS0 E9OKOs6i;K]q˼nvzki( nA_:/eD+fٕa 8Zp% ]Ӈ p L >xO{t)pPu7c-R7kkmX\CefG{JO?_ySI0 S\n\yy ^pp<% Ճm982 _}$ML'|G&(><\Z_]L_e<2ToǴ=do(^I_C]~f ^Fy%اN|NQ6o.=^ԖfK"0vA/P↓K:r] jXz;Vw5ZU2i7aW8Ɵk8#!r%ծxBÀL 8~28d) yg%ٞ KwP sBljL2ֱ-7Gxrv3|=їв񭡗12)WIҬCY_W/pd9gGovAy G;'D{ҢZwBv{um[{/e'/w80 ~ }l=Ώ6J=s|pk!۶_):$}'+U/} $ m>1YK<>xH&>Nfd$$R9o~Wj7vzlQ+ݵ{_C˺:ԕ.KE´E<Q <.myN 1l m76?m^wC⻾m?}h?*W~;]cބ~m$ R:rvYx9_5N?{&Oq77. x ,C(n|/yCP1'JtX}?LCv UJC$i-؆7%D_~ 7`{wTI%`YOUK׏--Y*U/{m\>DDJA^X9iׇ@[IRq,|]/ }jfKPJYy1 蓾1SaE׮^vbKڋr{u3C{x֓eS>%/jJ1ne憥;pS|6aC8yKLS}闂7bֵC7%tvb=03 y )_[]?Ep=:o|XE(eic恄wߝM=QgS$=i-n.vpSK/1M¯er ͚SڴO_KOH'!-cN.l ɋ";d-)'-(8ֹS5֭VW>kê`G >gaX̣<^>}\ o~8!i|#~^F8/g_!jlǴgVpu.)FÜ'.p"gOFҿӏޏUTGx 9S<Nj)[ ݈Υ_~ie>6CӁjQp{8^}P/7oM~(]-,؎R>x ʄ>d Ut 3g~uWoxw¹X^ɒp N^xb/v]W곂:| CSa-g}BmIi+uՖ9kj*oqiOԯ7js ٟmb(}axly p("E{. ;`DS7p:8JOcKnv> .75P},0X|# a K?$,aӦmNJy/BYJ,W4EUxCL-Ѧ$(tw0}?'dϷ1:Gn R7 NK7n̛v e("汽R[0 g9cl sw+G[픾ik3Kx x(cļ~,S ˇLUe.rmM>o76 ؞2Ҡ87dJ@_Δ<}pr 'v,ل2w2|ۆ ,@7y/* ԉg}ޔ!I^h7ZKSoGvJ7~m$2'u9G'fr||<}y#ɸź{`_?cxqhK}I^#㊏GA˚>޼\?Q9t9Qv7gtGwl3~|k/$N۷{9r%)AĴ59)fE@L1bΊ*DӪHr̐&}s5Tuzy{r>Vuy&"7;`9F+O_ơ21ơ~pQZ #:I"> \&ÁB0ͽs$Q;Nk7W(Sxq 4RĶ3Rjp?(GE:Jt?҃O{@'Lpv>^m.Kp*xiI-δ7nڢ2׃}cxx3YCI~~Yꉟgmqmt҆vm7İ]^a'^:jc@{}XmM0@q:Re#y߼,zAˊYqeFG8\p̀M@x>8utKno-y8=<,uXy aGJcA4y'l8 [O3 > [KáKO~ċF.,[ Bs۰k?-~NJ7:t׉/a큗޴uDc-vuvM}wp=< kŴ}p-؏{@oǂoZa8orY@]b~_V(֪S1P8Oo'k?V>L٣M4ʲ6}wخOb7sa pDM7Ax(" gΕG],1]7VneNx= , (n"7Ȯah^`s\ 7gG")CԍoZ7/b>č_pr]%~iWbi0 h n:1]y^DHCOiS;u`Z0n_ _[C}%>^XHkKcKWO|0= x}gF+|66mxiGaэm#eD7o^v׫c8/GJ4|) u|2=$ˊ#-ͳadoF.ֈy (qKWg<Į_IܖoOz^c47ߍ(;k7]jiOOן aPʾp<օCp D6E GBt+ohy O%eFJ=6(ɻ[)f|Js. [ a"ZT~Nl뜳?ˌ8/'#F7`\:_SbKYNOlzˊYSox< P)xxm>A*Yq[֖xuc2Mӎč_c<$uMeJ\"\ anr͍4ApG~߆.nֽp,7j/1dx1ڝAz OƳԡU|vۙ>*f%=Ge`*V([}[NxDl'hvB־}I=%y|9e!Zw8F?(z rQQC,ɢpC <N_;wPB~=fs#xF'^6u!tnY>)AmS/4 _˿<@3OtG±:؆Áqw8 .gCb[SJH[![^P΂`Omm/!嚏90}*]xǎڑ-Mq#?Ͼ 0My(.$?ϡe˦2,,)SB)\Ydtso> Ʈ'p OnJiL}t7[:*.LIa-;oR[!fVuhIw^wP{.7&l-mt EY'&Ƴ>,z.MC6۸Y!нkoGLme$r$_ڇM4ɣ6>ofQ*y:O:by2|z8@&8W_|E1܂KltqMY$=!|\nGQFK6u"}c?'^*~>dS<H<] &̱q|3D|eǼ#s=w9L> > >6ELdf{c8,nEf}r7[!79v!M7fY%ʐ60XnjZc*3;8v5@q#G*07xCW`?"e:mM/aMK ܔ;"[u\ [~8v/]p8c7Ndk}@yҿuUEqlneqy .huo>Ά,pp /`:tcmlDU_Re~N{ON<Ԏ4L- $m\LMW)Q1_tջM=_-s8 ~$X,X z{HYq0l>1 SL/ ;@4}ZeҸSnzlq-Q`>֥1v l8K$F/ӨG2&q0엗m_jX ~BP;PK5pA b@0ʚeA(]]s4ezoo3}s~}X  :6뙲s9[S72,2ő6״J) S-Ik+7SP62q⏮XAڒ6X8-PJk`<4-kc0}>l//鵽 ./o SGҾ Я4-B A< f`{O%hHXWW4-Lf^e襫ޔ0;o׳Γ}!w*RJ_62m3p3ˊ(\'F72χߺM""= BV^-XR^tt$$ϵ&dPSa+C7q ΍˓'7}:$/d!kگ'9G5WOL0pXnxvTeR˩#p ( [|NȷyoFzĦ_=]98OЍo?{I[2cSMSƋ-mnw+MpXa;P;W.'0_ez JiyѦI&ª@IDATz`q6eTd?az R.78717Hq!o_ԳWEwr/pp;§ UR?㧞CWg$4nX@bҥ UF?!}p,:Gs|K|c/TGpxm %`^G'7n]\lKg@1|GCpLi?8m6}>u~EQ2&Ӊy8]~u587O{ uXpDioTچaKWk;9~"GM?\-\^|^,#R82=Dˊ[Mv Wzv' [<,E1Mpyfٸy_KO097^b:tc 7֍LI赱n=utiξ fu!JȪ`2v| W܆nσ=1}'xX{'o-GC;zyrut^xˍ X<#'NT<nc~ۆSKYnlkۀc^Bd=ͯ N .J|NnyZ^mX\@ 7oqxr$iC)#x1L1.({\ EQ.^V)Y!wC&Y\u,TP0biQx:n'i+klSҡv՛'bMp/D3p`#M`П`9_4//nJ[֣gRؓ2~ij>?~ b<"}>-mBb<߃ jkl-M%-ã[R7\k:Wۧjw>\^\ڔ ao7Cof; /j7;:|C67m(-|h}ߘ@Ƥ~KYqH݅MM ގ_]q>|sSFpּa9n6jMM|ce)ԝ{t3O@_3p~V3N^xz7I~ּ݃7W7PÕ^ط-8>qxG1l曱@픯v5qKvX?<GEѵ=g}˂Hn:6 ]Ÿd^iep_V(㲨 (p:?^=.wQ$^ \3!U3AdvuI204xo@1lk8~^}xn `_{|rMbݸ[#Fl_o8eRol0WUX_/8^h[`x-q?W{/?Ò]wq ֍I媗n"j8ڗh."{ڱij0\Pʍ!z+dO7c˸Ocr ' eyi~7>HRIi[ǃi>9y=~( !b߿ց' 8CWed7J]D o/9МaICl egjy"7kYB R#^~Ű+xZv%z}@[~kh%im ΛnzꕐqA%83ŴƑYeu0ܼpoh.qƿuKit7;+qv_ SM4^k%;'ݧz!ý&$xH{rhLV$u-)y^%ZIY77@, x `|C2N10 n^ h% uź!`]Hz*ڣ׆7?Y΅}zl 87e6|oX  8x;ҬciWHzwn2mSʰ˸q n 3p ڝo9g;ukx8cr= O4phGHlr!8@&8/,T/*šJt7B_*<. ^Pki5fњˇFpsd)iK7:Į܄5_zW/ݴ ?@@KDa]8KC[Ƶ.a#R]0>7䛺(P˥d`Yڌ#XEm{s˱\> '|2sg v>{ bXzQp7A]%cyi m2NVS`)u=p`[_ p"~RAxĮn].-~˵M?trFʣC2~/.] EaF]8M %qcIvKI6s߭qʴn>2:dD[[bKus/a4nd>YLHn J˼,=<|||nWBDoih/l[ۮqʭd}ai3ZF>5˦~ .*x6xI(Xb\/TI3:\ >͚'jG~ѼLc- O`ߘ_}9o1wYk#zq,^uh_l}per*zOjkX<|[^J(Z1Z?Wsp8̀AN¦`]R_硯3 nXt]Mzsa{ wn{>Lt >|#D l ÷{:zfeEq> pGߋ{cSE<4tKJ['}_{{?v/2^p|i`_hMJܖ- (Lj,XlcٖtͰ {cعKuŲ8 t]ٯS-MùYlMM*D>a[$ay<7MmM)u-K%CD|v2oO6J n`ڕnVHcdl16p\fp)9xx~l 'x _mgYKmn96 7߈xlq4ħkUuv>e;IޗlxU~Vdat[h?OG=BoqoM7hWx\ʺp<ܸWai}0J8%)[-K@מ:vfFgڷp x': 'MÏ$'vEup5xi~)cj_mvH}PkI/ Wr-ǢE{Yp霄b/i;bErb-ix4]oˉKRs? n+(p3Ɖii{xJnS~7{7pd36kC!`G]7DKgx)ۧֆ0~71JtݵPX;ҵgCڱYɭkWֿrO@uw\ x&r!?|^!x=N0˲ ͱЦmKf}ց3E:%.0uak8Vw~c~e襫?iͯo/#$~ȥuHy)CY_*Ѳ.wrN!Y CC)ãmDx pz]zk\eJ{]nJ/K9F%Lpsm SGRn 7}`܍--ur6V^727םe3u-ݦݾ DGq,KX&ѧΟJq̆+qʳOz{vz{`nr<+wؗχÚF!Eہp.\/}<O/֧%n,6x\+b[}KVHqo=aMNRƉCu~bum:ܗH}BFEO-uS n,pњpF:g\`̣2=:M Up5π 莯s?Q;zl\8}\ +y`# H#ڂ#p#\8oki3\w,!^iWXR;o sW[JywSǨqQlA#z/w'Mkpxf(I~1ip!KQ&rp3ͣ G+Fep=|􃛾x~.Ӌ&魷-mno/gNPGJ{tv泬ă|q˼R7m}l/ p}>Dq䑃{,Lŵ8pN _Mnik|] zYπwAچ)+嚟`zlq눣ڿL{9\ 7띺G)譐l__cd,"d#K9eu* nRU VK98F;\5@u?MY1\">>xo}RsΦn|7 %UYqԟo'uiK`>na`]zFJ=ڿfm_᡿8O"_Eb`r#;ys8vRtLG> ηMhڴ)%Ww(0O /ۯ?㷾ڞ`|Û[Q|,DZhqjI~zϥM{ӯ/ˡ '|9fG5uQ KA? 㛅2}S7Ml^fm.p567׃񓦩i"?sS%'n|}d0ml) ӐmXI}اyX/Q;zt'D *~mSǦ_=T<|}["/O<_Z0 ez:;nf8NA{u%|<=g|5z(}y 8wzҧǥ%lIRtMag8/Gt!yv.m#Ǎq8 "XRF;\b S4 ] 7m6-/O=gaf7M%vu7e1raGE)K'隮isL7N7w_7ٯ?:TǴXb%SӉ/? këKDX<8}*| 7Hb/m ˼Mb///ڨW&H^IiiY?ǬcV@?=hrq $67HNl?A T-u:aO}ݰc~'R5M75m'  2!׿Tk8|Ra`/^hx J܌$ĺ+v/)ǂvU}a;GʺE7{k+9ƛ w6|˾<*1}gڲf=,q0qOV|p m!P?Z_-S͟.a#noߊz{Twlj-W teXE>./n:08<4똃,I+QM7`~5js|@,̩m\7 \ uZb6w06$o鷞瓢[q|jqO7m5%b=׆a=׀z1|rӧڔԳG ~߇'H&xt.jSW?pvz@[F8w^zz?/u^ax|>΁+-UJ.g8Nl7ضK_YnmG%u*׫6eGSkLU(q%ŴAy6|lKNe;|yXFc.eSXlM==iK~x'mOCƾ[I5puռ9IS߸eR@{\8|g^o:m[mҪSV6yr߂jWfn,z_ j`A`λܦ̀18E 3< i^yӷUtx)<[A\^t7F4r%P;zӖ0]p|/Q~@l#`xg ƳO,S)f%L,A񚳺=s^z^0T7UqMqFQFGed}E3fմ[?/ݿzI{qo}?Bd[cr gwM&q}Ă{_5՜gW?ܶںjQᵚ-=n^FoҸi7p(l Et_͞GՐoSj] ġj[{qQwxJ7iy]nol}㦾q˺:I]Ҿ>Ŷf%7p2l9΂HMX7,pn-y9Q;n:=y0ӣnSX95p+kp6%mO]ejwVmcT20qp~7W|v5&/&3a9X̰I ooZoV?wjEkWd5LW1w9V꺭iٴKb@gjkWÁճ&۪8ɅdM|82`eT q".y7Ωygx{jաC7i ~2<ܞ {\ypSS^0IՙJJ_ ww!eK?Ħ[>.#QK¼}^2W?} i{eU[^q[1Ch n:i1Tk4ƽv`~"-غck9?Rv᭐vs:|k#Py/֥,W/.zJ3SW? #d[x^op+)sϤa+ {dݸ-zWWY 3kK}_} q5xEՑ*3ژ[UMդoYҮ_y¬/ ?ìW-|!Mrڼ.no;^ۇ)s&u%^/2,wSrZwvWs+0'u5iY h繣,ܛ:! 7IӧztU6JQX0n lunuJ}S˸$H;TR^$tŏ^ ~瀗8 _ҾKPW)ˊ^Mص5%unwm7<^|2 p}8W~ur(kB>|.kRObI'd4{ݪ]XNA}s5h]}$\Z@~-zJb-0VsvɯKvYMeb_+׿V/,أ|[_m4uJC̞|Ur[5˫-"G]=3&$WfKj5v-٠\m/"2Rk~jdGRnZgie'Nwdusp^"57^ۭI|Kw4z+ug◶薩zoŰ)<>cK]qwhW[c@b~A)[8xMq#If9M{ꧽ+}Mٺ<??@Y!✵^ / Ι౰xi,pkr_Jٖe^a[M}ku6U/]iJu|Nj+.cEZoze|fU񉝪׬Z o '?~X!;{>] _ }tѸhVbOY0՝S[o^zٹ6ŤV݄S{qgB673u@tôd^t| ^^Ҭwt[pۭh]F -p\)t˺E'j'r[%9%iW\5۫-uM)ӵ,{<lW9ࡧ~^6SpriR2'֭12̖\P ˡw1ϿZWkrmώTS0:iʂ |9îi~ Kjb#o_ؖoM۷ί>Y+WYZ/M@)ԝ+n2fL8 gUvs#3m/E$tȬ%kUpXRrԍo o 7?[Jh3?Ѧ寮*ؖ(S!H?|;/ؿ_usŞ(m 3?% a~+9+؇{姯GW3]Sn5oCX\´N|\+w/}s˲?Oݓ/}O|l9eWSO~[y WS-DE^^;3깱7' *5M`i{hyk/967f7-OV~ E\R[/wVJYSmIˑ`'n^TKK)4S utKyy)Sr`h_t˫l菭㚠kƺ:MIڣy? GI],ߺ nΝo}4q1(e:\cۚʹϛZv-{d4_Kar.ܝrZ|'9.E6ÂG7u^fˋef>e]7j/,^nMM=eG:&c=}z$XioD'vf<}&z (v}Њ|SZn6Dʸѓ_?;gª b9_I*uԯ.>^ :,cr=Ï>kĪMvvXc 75&?f;28\jhTvNٸo""D!uxoML6$zR`S\>Pp _An n>70xP7|9dX .6]*J97}.xPx[d\ޔK{tRM,/"RKgDZ\7hy{mD%/ 71ޭCE=u*uÌs$<y%.iw$z p.M])uY`CO`͹7RI2D%i+):zq=?;IV>.ǁY/$&V|Ǫ~6,ҹu|i7\`}Y!to9z̔ooe?jLҚwAں·U 87l8 |%!k:e։FQ5O\ ۂدp%jNtjS=a.YoAEqx(eC+daX3^)sYr7ڭ /9S_OyxH3]qp?:i)tuk&8玃p/Ki˰ n3ҟt_{RM~.Km,~/vj84fx\M4 Lju &vp5w6=op7 rƻfh(?=y_^'3!ObI!?uFe*f#㷯$q%tX^SJ[6VIޥ K ZO^ڎXҫ#aq\?n4Dlþ3 ƠOE̻-6f^ o['}/b?sj0|: *C?jp*&r177_U[AkUX1vf50~^1 ?[/ُ͕Ie=wggCӪfQ9 \rܸoOJ-,4zZ7 7m9`38O2,zFq6LgV;k}fxhcZc`{+.A4ZG2-ҍ8=4Df$>?q^>:i;)H^M[7$^!p֟{1V۴6XG@`RҮne4Jzf{p\J{R 5Ww8rњMp|/kwe"4a2b+2*?%Wy_Y0d\jd28X -k'r_m5psr\6 x|ւMl(㶬 48\5|%xE|kZ[a^7>7jI b˽/z\v-/Ṝ/bގOOf%UnE[^brJWG/ꥤ`|l ȷ8ο䛴Y;ږT|y4Mk^=m'n^}uu> ǬVC0.K2+7I̕yM:|\+2 X^ut1(6]Ca0Ķ%=ԧ[y9NJlMg!mc^R;_aPoo4L$uLeX›6SַnHWV?NzſVRtmD m﹭11oaMZ}x{Z/b?qa&җQOz:9M7F|z|{w&TN@ˍǫR +z>5[VHPgCf/+#p:n%o%2䧛96 x 358q1 +wJ˞6ǖ43vzwyhŸ^,|Kbn`^OG|rzg'R-=n\71m7A1Poo[Z+4dҗ{ͥ/vAv?v?AL[?.ko\>u]8o|znznÕy|އTg^bhcoe{iAƶivG+7낒RGǧi+ _^즷bCi[OxCo֣쫅ib95tgym;M*I9Vy9`1F4lj˻*8_ fU-F6+DTc6loW)Mz$1yIb&B3|Cl;1ZS =uLYHohwj%d} @w"/VSٟKe=sV_6i^58-WEbN gՅ;F+B N辌ӿn?F7t+lV7T @>H3<$c!nBo[sAZHSF}gYa~:^_c-z+ߞuLĚ4_!Yl,46/@0|tB󨏻keUWX'XX4g+U&ֶISa^{HMF_[=y__^D_u;؊u̩Y}j';olaG3 7SAG=Tσ#P4DRҍn·7/)%YNqHtI6,nIº &`/Faj:RC$,n[5x9`ew.U2W5Ί&czonI̞Rv=Iuɡ3̭YnU2 YoJM+HFatE6bsZUS2Dj9蚺mirxُ a%j>{pk0_ainyZ,XFҡv{lM OXlM1N(J[ҵ_|,Oۿxnؾ#$/kkpL=ۃu3NVƛpz>i2V3?ŽU/g_9\\ Y|nv8bjclo_?mUv{;Q&:JQOJ5 ^k`Fңv5g{H2#)_t]Mi/pR]+nr˼jmC>Nu?23殒|Xab 〙^>OP/# gj4āUW|?0PhxǥB+r̥]V<KžŸ 2uaZwEnA{ҍ{ ½l-vǘ-|G##3U=,sDO<݄-úkήJ[C$ 2fQ8*lszm?짶Z؀ڠ`~(Bg 2e$̐!T{;Ul|SFYʫI3J^+кtu2r]VGOIyа__rf'_bUon*b08W=FzJTQ|UМ QiICi8:@˅RR?7J^OS3: J}DH\^~_zV*U\h}#mL~6*ִ `YQMuN@'Uڦ cKoX6 ^ܸ0|B;g~!i=WYZϪ:h2?v+"(P y[B t$rE+9JZɟ>k5P|P}P^Rd/4"?}RWeW1V~Za. QO@ړ4=״GZѡ_> B Jˢkxs4 5!9y.^Thʢ_ލL-e(atz[12ոdž1@ˤ 5oܟvu_~,E) x+ax+دSR@FRo/LeVwΊ' N^ =L׫ O1q@$k)t>Rz/e o -R/2t+s@vʤr+VGǥiEם`Q"d0 I:T!|Hh@93fW#wFҫb1F^Wb^ny;x?\2Uf\ KHjV-% ݻnFŀZZɢNstRiCeZ:\?esEߏJq'~HmܟBtH딯RnO0h=z:~O0֏庤ϥ<]?'We&)@CkM@ipH{ip9Lg A AV`MO@vXL`~ T4+c;Ō'#+Z0q e|r8la?ZX =l*}r^O%zZeCP͢~>ԥ\~?uJtUn0Pe&[c->&H**3@LܼEgOSn)4Iο\ :+jWC:λ(@yx"Dϻs?v9_cue 3Ity> %R40 >%2ЦU@F⹤U˭1x;>4Vli?Iې&k);)+&AHw)2o:+#) KmGZ9n0\5w ^z<Yj_12b'}sJBFJ1|׬^w%Vzv(Jۑ(z4kvu RpPN+At庤t:&8!Pzkq^Ys.Ƴ tIr _&ew> PڴÚS=6ϸs-9u3m?@r>vL鶇uLLβշ$n#9Yf$w6 zc9?27?(Qjrp9[dFIӮ褘LԆd& \_|Η)T0<N@"@Im>_GC4ŭ<@ C|%pBS:[pPF%?:t#8$+}6[K23|i2[) b`Bgg B\;6g?2Aqp@ hx0Q=WL3vw߫gLC!]g ]Wܟv#R> Hۀ>PSnz:j#{zg|P5zrJ?+uZ çRܥK~׻4>4k;*5 Ȱ*8}m iưkGqדJO-@*ϯ&4(V,=]TE7@?_=bc!w),o{텩\pe_.$QXVꪗ1N  k-ʢۓtK]Hǀ @ *Ư㐃d2R>FU {4cxK@{Lfk'OΎRC8XϤεS8#,$Xsm0h!=*O!``|_y5]LEx沆oQ3*Chw׬0%g-Oc?z>I+kZ|{ Ӯiʒw=z z.ǺNJP=;\ pQ=sM/ޱ9H!OW fpHpϯ#wW8(x| DzKNi<=c"/ihj-O6m?F=arǤ6QaLΪ6kvӆiL]0u醭'A2graXHe2e6I;MV3%Rqjs"&#jJDuoiuOnpFXϣív(\,s%)a7H]Q7JI/@&J6/t+s@:u5ckϟ>8~^z_Ja0hT›]_=2vEmOǏ= XiNGp!mX4qm~;f=eˎhA $?)ݬRgV|na {Ch?ظKX=p$ؖYg*m͡|<~E󼗟R.YңQr+\Vj,$H3(|mqtH]+kvW=Z=R`''/Ts:q $ŕRL8/vƯ8[R: F0p2*G[ΫL?VƗ?`L֦f>uOԏ\Cڦ"mT %gO*z*uMt֣y*7hyDU΃MwrՖ8ҾHxM&M i'AY̚\ڍ5Ksr*%yԨj1Ces6csWyg> | $#7}߀)HQ*MK7'hQ;+ܿD9WiX W.TPP)2ψXiδvL4_T:&S&kS2#`tT9%-F-^KE\Cnگc+ _Y@=9xi~ǂSW 뉦9n2< o=Rf O$ewu[hEF11PM!ȀW^2ꈁW93]ZkD5X hGDxyH%;0 T1kp+tOQ˘//vp8w#x[4kʠb!P_Fdxx0[X @J ]c@ ``&s4YQ5B153NJSXXx/HYn:'*)0W~_ro8=]?2(iW~ hQ~W)l/?}$}ORRtd 31#F0 @N?/H/K[EKJz<)ea:~ PJ+\Bp@y t^yx>oX ״h y 1 !`Cv G\ݟv05g$u7?ﮇR℆~r >qXNR*r|TA8p6+ցS3@OamMW` 8H8H#z0Rd.O RӁI4ӖR=xq ,~e@zd G~Ki hB .pK:  7z0zH"i~)` @)ہI+׿|hd Jq*gr5p Wi|r?|| hC˕_a": CĀ*zH0 ZҊiu2$5x?*'$ĀW2ei/#@} \ Hĵ0hx^ Uqn>/B50 dFNCFz%8mqZn;1y'0o|N޹oRDh딿DFʢ| N W4x_.i Uʐ` (^s&p7~kZ@C麶1O/~1 ,&ÑSm#m0ȅG4,Osu;$J%V.9b^ OOg+!vooF)/82^`rC`P= 2%C)P  RjZw>.~3+XaW-;ceʻy$85]Oj5(|Fljzcaaf+CsF[{}|볶fstBpTFk.XrFG_e`2dY@ 2ռ^h dXٰcPymj=9-vE6~֯UB1 @f ~櫄S;2ﱆgE bdL-$\ f;k.˶+o$/%e@)2]e`KV:d&~>{_"d/Fmf:?zm\F@'[ ߿󋞵I,2N)r/<@  34VЎ>7 ,xl;1SkoE'"g.)}0꓍3 \`{ԐA`?S-7ɾDZ|7}RQH0 0D|d tlj#HYLg~ORs5^-oN|viα|X-/7o>׸>ĀVϘo77Za8jǠ{X}2{'jyF7Ɏ 7 'cD_e@Ջ>}e4^˳nBeXЄ=UdW`!e@RwYaVTc3ٿ@~^$20@H,22+^t? s V+3CFi07uBL<~gwΤaM޿=#O'G T4aT̝m<3ˆ:lS͢ HlLю{@}b&x=t[d Đia}7Q`_0+tڳ/|C/`B`L P&#` -5 S+$_b \Ql%\>F hl` 8 p(ٍ`eY]5V<`3{H"@7'ݼ Tz{sa TQKm ^j10lV` ==#` ]dnL;5. ݰ˄D` 11*#`hlY1_~HygvSlK3{ؽz=zWq|0 2@)#q T 8FZQ_֓2&MO(F1aU~}($@Tf`9VKowu>s]?0`nhE6}l_ (h"G Џvju2P,QflC}f+[,;f.HFD>@}!-m%gmҡWc@dWuj|ifC)?e QH0 h|^c`z+n\gόbp<@vV>Tkfᑡfcʽ2|:=@2Y]e$N ִ[dbEm[7ezF42Gق@I}V=XV7 YQk*u@Ɖ\sL^f,hw7bC``2+82Y{&~hz,'|Ͱн-u*$sX܊/  jb f8zM- k}=H=k[qvˌݝZ׃9\Rͮ_;5njbdV/؍@0@ ׎dQm{l#h95)VxaP2G64z!@0P! P!" tgZv-v6 e;v؏fꁗC?,SSǶme/r* v]Z"0l6>iEгW'xOZ>ΊXf^ e~^l<8}24 Jb JzQ` .}a:s/#X|+NnwmYPczI1ky/CK^d ^*G Zho.7L W}=|m#ӕ]Jw;.knYޗkFVx Z9(e`"ˬZjn}k̾zw%Ka=z{دXIyΟ*!$*TFႁlryb;^6l4wMjϛM}l F5!P/_nM5[5 [_2^rmb` d:\([0 t׌3 (o5w$Nd6īPOϣxS*L~>`!$0iEY.00sձu>h?>IaŠ ?~G4X.o,=}5 lS>f=ym<@KG&"ְ`xO\@򤢜@7U 36qI?0A$>*6OV[Yx7`R1QYF2 ٷy/7ZmvxCvۖ{n]lE`,e_Blf&@u2@u>(u0pP <-z `fe(a pHYs  5IDATxU噀)4i )(TDQP-ƞhL6$5jb&h4Fc4&]b5+v f0g(s|ϹwIijj^cRoSj^ne?/_ m_RuMGE먁iR6Nϼ>kwXAW YjߐTDEϔ?U,OgSSB]O7&U Xض_Zp˩m|^Y*X6X(Ke_>4aR^w}+c9WF겉p)5:!vuzaYZe&ޓ|\VHgiMKa_h*/4R3`nyZV+U&* mt$`ا+~gYtezsHuz=ݩl- `xotkBZ8 !5J-Y9K+sd+ <=8&ȔYEy?T4OTnDl—V6pxTv堔x#'yz~a:-e X+eo(&U'v O e^iLA*?{^ITO<:ܲ2fu>r=΂{v{hHҐBj3Ez;/o P x#mϏG7F?Kv?ydv[F׆AZsK(Wxւ,X%BZf2`c :vyE2$!3ܤwcl8Z#NeeZx%xx6ʴ,!n `8 qOA@zMcn' ,q;6`Yx_V3to/Y>5els*Sڤxح4^vF_ms\ `oA[|\p  c`s.ZzisooMM}Cc~e}Ggs.$sch?>]r k*\!.^/۟2!}ڳXK ݺ[۶vrU5W׬wKOxV)oMPڃs9ׄXmo0ʟN k o|,UBWڿfe&nsD&VZkgb5(m*n|!f 8ꙵ]*l e|\ ]SD^q}>p4Ϲ}p0lo b[J}vGy&R*TT8Kxp3r\w<8K|AD|8~8T]_Hɛn5[AIs;08w-Gp| AquERjX7(M&Oź#\9a#tXFԶ͸tnmpnuj[e|T-y:*ƍIW46erhJl힤lτxp;"(":|"a,x- &InAƹr$f\i^55>4[QXͻ> 5PImR뤡?g;f5v`3ūҸ{+L\]bdbplͭ9`:koKJs\ ocq4yYnn+ >p^V[LxZJY& `0OgFLw iA֩5z o. C ܦn|(1)."rτ#_\ևL`>kKyԳ s\"q~cJIt SgZl K@!< h94Ց:z;2qs1)` 1}hkYSM5Us/Vިk3gg,}C 4jSuy4K5BU¿&˲^nEWO ˟+Uq=u9&mpXl07.-d9b~b(韽8b׈>U;銲ߵJ3nX".L]&wW442I'sK4b^T=g1I-cbTvݛ3NXq\ &Quܽ ΏyS::UH"br.V,7TJ^{+$:bP7mixP\*;sHW~^e<;mn.MO~׶h%ofȈ74|Cex1 O+=Vsv3O}\w`w5_m e9ۥ:sxg=&u'{I +:AIo|1<>S*S$H9Lm a(b>[mN+۵#cД@g=WAaPtA[׶;+Yg_ڪTL[y,߮霁z | xNp:8nרHwJן>8nw/􇚧z„vP6}YwDimvM;Al +vhKԊuuC;B@΄KK@N_ Gqٮdy?)O.>ݱsh&;P~^HH'vev)Om6>HeIzmM+ZmjQ]ΌTiWXtIu@єaP\D::K^$qm5R'6ɔp1DrT<D0aeb])yԮ[I^bTFt|{6veŊX#^w |\TWJsH ˁM{TOHP`P?$K *9( ebyےMwwʵ}AUn$tWLmqWN9@Gs`&\&5=Z>bR:= L'Gu%xGp(6(έ+C4cg : )3gk<q bby)fh&'ˊO>rs; Uj޺پ'XL1a+U(JsБq::.gF90UsH(&emw]h+Pw9t`&`Czr9.ڙ[v}hKf5j?Th_:L3[peu#SpBc5Dˢps( `6 ]_y7?S!5`L(f}qa}`Kx&A@/MJ%ک X0 緝m2s\0>p|UhC1GxȮ_mur0j164Σ#08pw] [6uv:s_EgF9N`.50 1_K0뚬,Wp7`p5)K8zc(1ۥnn!nVsH9<*9 9ch+][zuI~ i8G:oz+DFF('aNy:q D3a\=Y?e .` ";R &۾X$֣h Ƿ)pLy׆CXPHs&6x !7KeMJ46Ty#=Zg+/<וs,n: \ng@H$Z' c\b$la`\@P@{c5`׆חD]ʩhD$FީCK~:ѣq^`t2 SP"gş uM7( F^E1qCWRɺ+Aw&v>#8F`_orIkkN_ZY΋>3zZ@lhI`28filw1 ;o]2R]>Λ?o[&}n#gSb .ʾAmJ~+&Pڠm!U:}<(&W:`=P\ O0A˗б4V9~&6+:)8և}~w<ۀY/+~<)ve3͖8?SskDRiaΕkmuk71l K82_eS[trC~@| @B~@s4mn%>߄gAQI%` ]H๎qTJaV y^A*Q3j:nyɏ~Px`NE[_Riw$"\p}` /"q-ח:(kR#>(&eueoJ!{'`rh\8c8*ϕ [yy1@o>5q;S/p^GN}=^(>Kxw{E&rO|80'/)Fk5NbUEkA@= >09cuq+PWZ[ PWdGg:sXm7 Gv } u yǹ PP%g?{w we,< On|ry/up%hee6XCAMc$_\RCg~:  8R7;>qw(LE_Y>gԬԪàԺ!GEDd {ܪ RAPшn+e9 "2GW[>/]ڤצx-ڷ3xh< J--Q.iЯ#&xA+w+S5:wNn!}23Êbqo=r0> EDj;"Vws9ɥ:DCC?#mP&}<8b/Ķ#A8mb.3K]i؊_#UzCŘnyt)}S+~}9 țgLAIO** 'hJʩtdu}Q=9xh:~h: SWVHqDcڨkmq΅7oe>%/cK=One[x^ 8ϏF1ygl1ANzڦ};/7~L(/)~ӻ$>[/Ngub|\闂[e6h05Tؐph`8 ]@CÉ~uч"JZt]\\vQ zur9XZɏض2bRDR WClu'c˴`10F-bfXOLG~gBzdQ|":Iٟv?P/A8 h3j'@v:A⢯|LpNoHB$vư/=8֩@1\c 7 jx&8pQ)y]V#MICq{|eQo![=ϛA'^2ByiBS=FA%GQ^_vK4;߂)p>1eSNNQDucֻ.}t^_п;L; nw̃PW7HlQ*{.mڥHC/ufQo|2On4wq{NUI)0]l[jԌMY9j Qh@+ S'CqN}mc+N}k+ b W|t Đ֥B\3ICuRvKg7YiOlaUVm߽+-59 gf6l`eٮL0iu!k?Awxй&WL~$wYT ;GCX$y;ݝ^_9-ǹuToA ;V>5B:3oZl7%wRbQpaaĿI( UyG>rf檌  AiԻugpJQʀa(V{A7J}\D_=l76Yw6L+:Do¿aׯ$n|`5G0ie'ɤ^.RqAX;z~?Gh烋c۟OazF\*^Og5W^W3r)un\!gǀ*F~&}v c7hQgm8FnUӃk-K Kg 1DL_'9#k6u}o;3SУFWS(7?Kl|y&|Ǭ+TgNs#'s+$n_@_D y`6mP~](?w%,T?}gӞ8gZzi}RО 3tIOvӏ~;X&y. Nh_6EOZ*U6,=mPNJٕVB`{A ns|6!QUgpMW\[?:v+t}v>4j dFW km㣳Q|1ByzG{^ZOUꁥnk{(f z*>4--htƇ4j&p`d7\ a>0z N}A~ǩݏn tMc~tC:MV~ѱU1*๎֨*:m8tՁѕbՎ6(:}8s~FOʐa{r|y/v97LdRhA=4g:ck>z5_EöMSXGp\SL\ gǜP6 Nqw0V?gbPm=hZ#Ԕjje` RUꡭ;E6xM[`px>AGlq0q)B4fy>|qK$lLdo{&Vgp|S]46 @Cn:_UV*rd]έ7'`_ #ms.oA;!U$\H14 /StV#@xWYOOJ*wLu qŤV0uQգudxBwX3paZ:U7Tk N0t Б{C>Pֹc%V1x&EH,t 8휵FU&@l_kLX^] F58z{ @A]`6(ֽ;y"ȡ(9A dF| bKy;8 w{ 8֜Tt :DN]Dq<op7v$oE&hFWr<09W*|M+p ;MW4FmA7ErWjl./Ï<nŝ$&N]"[Jcq yc a4<V168mH Z$;x gڴBo >þn:QjG6o+׵JNGX:pAq>ǺLqk?ˑL蘁d#>#Žs)K1tf{}ɥ1IL$0k/8lgIϢ5je8x F<&W>x&:@(:_Z:Fz:/LGlvS\$9>Qk;>>HYf{>\4pn[Op(GCƝ)csbЬs\S_+yGNv19ʝGD۔t`& :8A ).R\ Co?%:^GPvSPH$R4F*޿mk@D@).Vb/;JlvXPL|ź#P!(vh"0[Ǡ-߅@ *>t`w `[hPA+6nN6a/xnW&ڠOA=ű b๒׭X@kchmxm &Ґ_-Oak y8k:~T7/H:V1w{1).PL$\PsE}A00̑u yQLQ0/y,| bpn/  a:zXnT@s8w,0ׂWtvlh N sjl-n ee$r~Ox.2(|*wv3MXms>Q[`}`"8Iu1yn:׾бB,_佀rp2 ?tDַ1 r,E%ޱ [7YoRwC1umv =/p> }a[G&SuJ>t833=t EOYۚ4N5:`H&2\:`Pu94 qty=AVGqb|F/$DŽlOA%+5u%AROAC]QQ] @d@T;b0K89JGԹ"8gQ6 ڤ.(6Dz:ZVGuU7vУcwAų&3@'*L4 Gp[ : ҹNѮv~K|W %Ru5YzQ!jEvL%ϖ2hN7\2~ y.'DXl`Dl[wǩKҹE8cT?ܵgp u1MZuR8[o{b|uui`VTm](;:[P4ܶuRoP\-Qwq HQ@O \:@4 Vdp?ɕmNnDzXV_U~8ߙԥx[_c;?Plˆ:GA`~Ap/}}fb]!aRI gZwj:8ל'b&qQ=N۫Qs@(Aݖ(;@p'(>PO{ݮKj[n&qkli _Sޒ\]c Jجcz 8tdSg):M }l4smt mrd6P|R;ۄ8Od9r~}]MrJ%^U*ҋ۳m~Yѩ5)͛=:sx*+]tCӂ7L##k,tO`#<$e< OAse ~5"0eEGքQ pӄ ݼy7x$}MjsoC-̄@Qwc[EۖzL,uIT/{.U/UM$ZҞ'(r_|gJyOx7' IxXFcXN׹6WUd Hߠ|:1 `nQ}w!RU 㪇mž'STm.Y]YZS_ Qzүy?wSae74^:9'Aج4H̫D9>(Cu d2m<WW`pcQ,nAuK>;z1[+dVWm!Fxb:q,vseՖo eGd@v`r}9< tW( l m ΰ>\c6ƧX#yiyhjK}n6Hm5f=p6~#zݤ.nوZ\̷FLMn!#:6/pr' [A7Е3op| ^wK78ޗ??~n`3l$8pͷf~fޞ//|l^.N, ]R~n 6-^ G|ۻ- ^]~!TW,U}jMT߮s4+Xב7cnV:UgSw(m?_-+L``;ܵYeRo ->e_Jְbj~'ӨtQSҘ~&7y}Ӆ左z}R?3ji(_Ϳr_?*<Ν*d_K>=uXu?oǪSFpT8W WT>+5bJ'[|k-?LA #V~/(f_G Feǩ|hǚxK]!Y VERH+R3ѻ2[ռ=WNܐ杺y*o_GۑP|W{R٫[늜oMofMizu}emUJ^=#uV*h>P W6Uud: XJ\t푞yt4UbYi.5SōﲁNs'eZႺ$ [*_z0-;: {kT&u{wHQ mh#_% 6cU;#}'jW====z#ӮlPIENDB`socnetv-2.2/src/images/socnetv.ico000644 000765 000024 00000040056 13040701535 020241 0ustar00dimitrisstaff000000 000000 @> @(@| B*%s,`? ~-_4?24L>9$!{^x 5<-&o~Ny99vzlD@ %q|+bNDmWC 8XcN/]G"z$tw4Q,M3 $twOd   } ZX"y &qj#t}@3\[ 8] P9 Qc'k[5F/utttuv}zz{zrrutqwtlkkjhhgf*da +00 wS&ut3Ps.VM5D"nzw- @ { { < $t33.P- h _ya9 |  u5\yg$SV/Q& w&p  yy7o&   /a9} bE|u~ -Mwh  xhvZ({wt=Y  O Q  KGvs &  ?xv n}G  % us i ;Yhi_4 l ur@  {  6|9:~y. |}9usKwp& rh\Xz_s )vtDv iz z*    &w  rq]  zr    }f| !Nf]z )s\yoi,    =bmsNd* z_TqJ}/8  { Eu&|U]xD{lb K] O<    JN ZE`rYsKe6G%r|$u~'73 F~+  }   w|  2t1 El2G$u}%r|6G/Y(3T#w{R  Z35T   Z59\ |]wa,a/Y(sk| z    v   u )pu }  sr    |k s   u yy   o  v (knGs= y    z  r4yE(kn&ooKQ Ivtf*.e    e37_ pzTIH&pn! cWsR y {y N!Pp * Y_ z3qn"R9 JF  P@ HB)xE| Xd  Uf d` w |  hj"s `Y  Fwz >Z,sv7e&  8V5~y9m7 qlP B |V  Y$xG   'i  }s@ Dzv  p / q   #}Lti %q~B ,mAp  }$v iuO\yj }  ~*mys 4b  8zx4 i  ll~   cy`aZ~   * ly T= H) /8 %NGly } ,mxO S{g. yk.LMB3tQ  g! n'   ?` !s j3n*4"==++-yw:A  |   MuHwi$_V(h`evTqG s $nSoa  1X4KJIGLFGd;B<P421184>3-4l52///0V"zFEDBDFLOyGJPJOHIM[SWPrVTQQSUXI!|#y!n af?b v*|M "3- -Y99 }!x# Op ? %dY(me=30SP>3 Bt Q7Y: 0 7%/XW.\_<< 8~w&  27{vF hx ,G: (fg(hw/WT$t{B*5K7E 0W'??7=?w?socnetv-2.2/src/images/socnetv.png000644 000765 000024 00000010746 13040701535 020256 0ustar00dimitrisstaff000000 000000 PNG  IHDR@>AosBIT|d pHYsS`tEXtSoftwarewww.inkscape.org<cIDAThśyt?ͽNA@D ʈ;qܪ訴u\jVO:q:ւ=-"".e č"*Aٓ{1dђs[>y_>N;ڕ%1Hla;ZܥsrwP1o?[oKyb}m̘ywfZky+iadvE<Ї88K4u<XSbAy :=[kI1?) 9{l{; FOh`ā?%A~[5*6~o k?@.P n6Pr;=P>~< |Hwށ)8"/]k{Xw Dm3EԽ?SĎc%#)ȂȊ@)pȳJ"3SBA| =:WdWO{c'KrO"tNseZfZb> YLcF֎}l40Rd Q~ .?8l?;Kp჉72y(?ڰn=`-/X|!df y)+*wNT l}(\(^ǁBC1#mJ jwو$~{](nE>Yߛm À[!O8= 3xUf(ab{M2Svf" )fعrDj)r|Á8dNdR&p3SJ ǔ}xC qDZ]<8_ đ8h9g;S6ežC\E@eK9q{n{߂g Bzn=.b]e¦s.idM Zhe yǞUh CB2v$MU|ρ3'<0U;Fjϛ<^V_S,M!5HA62[E|tD}G ЊB}\r1 O!BFHvF+Qb(,>A4s'")'®r M0Ij({0n-dhqXgL5J.ӈ]#BeB=` F#f XȲ `eLB!Kz!/<u;LE(4t|D@GV=;Y$\2[[3Q)aRo 5Mvȃ!`{P$mk(UFgѣ} sPR-pOy[|fr@kh9ȂE.A`P6fHKꑇ6]/D}1zz;FY~B2s8[BdQ8BTZd6HBTl=dbjA FS7b#@|d6^Xi#"|Sf%C6\ c& K^Y[BmRḆ󐇔6e4kAӢ4Vڙh۫%2d6=dZQA׶BCYnF;9vIh\5mG^ `C_a;"}/@3Di<[(s'Tsh[t{AB5ܞ&u2j wqsv"̶(ދBΡfC>c+I.sNQe&Vmtqt&o庨Ħ0r"mwQbFVlA^pUw,Qgd عvSގr6u_Pξ/k&k L}24y`;ɰ*,.z2owX;v]wlΥc/ A\O%{4/4#kJfxR\=b97_ .NZm'+Tm6W4*lWqϪž/6EVhrGnF1> xl{.cSUqNj+gts-Oto} Úa _w{#moz˯э]mJ~0IuV?eS˯G"08ot)Oϥ8Mp Xn5௽7eùjޤ^̻eҕ:~$'6puP*?R1mLUA}-O9|1X߶9BV`_u&BjHP;j%ҔeSxbmKHfG{IENDB`socnetv-2.2/src/images/socnetv.xpm000644 000765 000024 00000003625 13040701535 020274 0ustar00dimitrisstaff000000 000000 /* XPM */ static char * socnetv_xpm[] = { "32 32 49 1", " c None", ". c #B40B00", "+ c #B50A00", "@ c #AB0E00", "# c #CC0100", "$ c #B00C00", "% c #CE0100", "& c #D10000", "* c #941700", "= c #A61000", "- c #9D1300", "; c #A21200", "> c #B10C00", ", c #CD0100", "' c #000000", ") c #950F00", "! c #CF0000", "~ c #CE0000", "{ c #A71000", "] c #B30B00", "^ c #D30000", "/ c #4A1100", "( c #850D00", "_ c #9D0C00", ": c #761200", "< c #7C0F00", "[ c #8D1000", "} c #B60A00", "| c #A40C00", "1 c #A20C00", "2 c #A11200", "3 c #D00000", "4 c #C20400", "5 c #5F1400", "6 c #4B1400", "7 c #6C1100", "8 c #671500", "9 c #D20000", "0 c #671300", "a c #8D0E00", "b c #9D1400", "c c #CD0000", "d c #930F00", "e c #A60C00", "f c #3A1500", "g c #CB0200", "h c #9D1500", "i c #B20B00", "j c #B20C00", " ", " .+ ", " @##$ ", " %&* ", " ", " = -; ", " >,& ' )!~{ ", " ]#^/ (!~= ", " _: ' <[ ", " ", " ", " ", " ' ", " ' ", " ", " }| 1} ", "}#! !#}", "234 432", " ", " ' ", " ", " ", " ", " ", " 56 78 ", " ;&90 a3&b ", " >#cd ' ' e,~{ ", " $@ @= ", " f ", " g9h ", " =,#i ", " j+ "}; socnetv-2.2/src/images/spider.png000644 000765 000024 00000003754 13040701535 020064 0ustar00dimitrisstaff000000 000000 PNG  IHDR@@iqbKGD pHYs  tIME.-I tEXtCommentCreated with GIMPWTIDATx{lSeڮe`'c &Ӂ)4AEŻ4?K=DAD4!1(A@c?xvmot}}{O!miK[Җ[8W?S}cg7p yF:8?" T$#~hn^Ea;ۉ 9Q\_ʁzB%0+ºn40s`$=F#fppuB3"\s_x\/e/; gz9EKHKǚ`P Ln~?BUaP ̴ K;Qۀ% |o94`3 Fjv;w A1>EQ_S w6JN*(? @?`k?]ldg/X ׎7X HdgI:ZĆBz`3p5je}^Bh)7-ٌmӘV)5Hwvf$KW+PDVE+]ƩJdH --XΖs;fI>6$W*y<_yAF{IVL6 \?x%<Ʊv\v7uri!6wtTaR] .Oƈ4)ǧ 0WL;,VOnukͿHʢ}r|x gx^vTjF7Dv)9{!&KTr ~Lyy9. ˅&330X|9`)`Ffhw|ܨ~GuVvHkZXXƜ;nFE)yZgK-rmt?ڵYPK01[:h&JdD-~S;COv&7PK5I5Yg"t9I|TE`$jȤX_ޱH21ښH*6sjs8.^.''$ʊ) >b-q ˷s-2$|:Rr-m=pиRIENDB`socnetv-2.2/src/images/star.png000644 000765 000024 00000001276 13040701535 017544 0ustar00dimitrisstaff000000 000000 PNG  IHDR5bKGD pHYs  tIME #stEXtCommentCreated with GIMPW&IDATHŕNAcYEE+HQ¢MSa rJ2P"Eife=W)G3ν]p|Wy_h46׋uAr2КNHDd1" b'GG/gI)yTJ&<1QQⵚM}R F%HT⬳hSKkhzTJn ghVɧzj(聪i]1E&!S9MF[ݡ]P l3ĕ Ύ>9<<"r4*Ƭ:/Ő(=}&lGk^^ yMGR-ۏ|>*׺\r6`Jp*kzOOO PGp# `?_elUxR׾W]]a:*Kՙm;U@A@EEAJڪEJ,ED =d!$yYbHP 1[^N=;3Xf|;͏yI~=,^>"J.Nl `9֟QxPVM+ &K}>Ziߥ>-!b)@y`2?+Kr+ƚ-?Ft qR\0f"1`^>@"Y$}LP# l 2ۼl5/Dá~O@aigxNʋN{ڋughoaK-4Lbebb@rE>XѼ @8,ŀRKQ{&|XA{܍&7V~ƪJVWyE8 ,N2!v_BUшbüd%A+`!_<.3&CM㳰0͂K؅%.̍.,#b6 $97 4E~3yS˄w;;NqXT^,.{E$-!n߆eN}@pe|zRC<=o{PEr=]X; t᭓x; zNaoA'~`ǧ;=!.9bwhIsr0˅׳,! oE0x0րR@[yUW[>u1LRO!NkNuNqg*Ykx^}Z_ć;^}L7qz:0{k6^Jҁ?uba:@[anB RO./% \Y$me`(|zӖ/±.e^:֎I$BǝCr_9H">G_X/u~oC9ys[VD{c΁FeI^V_.#xꇚ'dc l< %.DƧf/U ?#0+ʀ'80=ցxj_fŔ]_#6UtGJ3BZ-![0`&Li{xrw=Fe)hZ#D7qiT;p܎ӍrH^'tp D$ύ'>cn&G7`Jt=c~d&Ȗû4;$ (k sHvLF_ 0<ƅ;mx,aZ+4l@OqlR./ =iۻ`iS"`*'a@Jqϐ_|a=cBT&}fÔ/xu  ԙA^U=8U[Rd!' Rb94/KW9ёLh&?xȿCՊGW5w,xmbp;^tXk^2-8M lfbmV ԖHHH&w 빀4>%?|Is[3y׀0|V%8.ʌ ;(T)LrSDDB13Ɗ1fz6੘z<׀i+r# Y  [L&,ȓ!}yX!kXL1zP9q' A#s #˩ĈRkldfd ͇L\‚jPO &@T$kx((ʹ%0rv3q蠄ԊIZ }*G%"Kjj-{1 CE8H7 0p rՎ'Ő(>&6(km-A҂[bv3[ ZkkFܡ|A^nW⋘8d@䍥v$|ACDZl~WZ,5ԢnA{k;xJy{MF8n"#p K`pt>U |eŐ:^*:a\pjk!|/IgPlѡ//+k`Go#@_@ $g)Cr  @sIW*HDf&xpPWs4Q_[2. g8䢆/Iyyy*0yCai#[Oз¼-@:8\r)N=ܾT j!arK-ݒ]K>}>J sUݑtxw| L!7@7}ԅt=/A@Be {y<'?ᗸ:-nx~p wKO qS9y 71q s+#_пι IENDB`socnetv-2.2/src/images/sw.png000644 000765 000024 00000010253 13040701535 017217 0ustar00dimitrisstaff000000 000000 PNG  IHDR szzsRGBbKGDMM pHYs  tIME u+IDATX  NbNFiOmAQA KiOX8s OKK Ojyjjyj  jyjjyjF f2";A:6) %}<Q'z2mκ~J]J ? *˪N%r m-(  %'Vx9 a TP^/'E~  D ^3Y|wJDž2~qDA  ! 8@8 t?/"oK tto})&&/AQA?  H"*ǹdz[^^ <p?#_ ?PP$4**7|@@d )%9 0<9й ShSjyj58    %"  Ʒo Y0--]u jyg :J~dl&%&l@@NX$eqDUDL hE 6 UIt ȸk  " *^R;ܱ\  P#K$m /ϴ  & 8Q I1Vjyjߨ. 1 ȡ#?  J]J ?κf  @  JɜPIENDB`socnetv-2.2/src/images/symmetrize.png000644 000765 000024 00000001504 13040701535 020775 0ustar00dimitrisstaff000000 000000 PNG  IHDR @sBIT|d pHYsO¢tEXtSoftwarewww.inkscape.org<IDATHKSa?(6؍nR$_A%TWB#FP'$ZXD b1P#F&4ŧ 7йәg}<($U=v:۩egi_E@DLuc4fS?=$v/";:xV.4냃$=ZKo/ߴYfbrj:;)UWc7  19IJOº`ًDxH!=?ϋNVhtnQB$]."2cQJAY̶zfguDBŁR-P"" <kj)p - l|bgD ܟΉ}d@) J_nan e7@3p> NJ*"y͙,cUr2`ȉ_΋ȗB><2ghf@+gyڛs@& CUW諬aQF*g|fF99}̤,+srHJ6z/L@C]GYjoU|p aB GXfRU]/\jq\XYMMe8?4DZKFD4OLDpVv8̦(g* R[5<9\FD9$PP PjD͑Mv~{*27KEEZ1Nm(Zqںc( :yB񂱈cQCEXnu/[L8 Bߴ?D!!)@SxN/qX!># 9%U+P}zCiOC ciOyYGNx1,S66/9nڳGw]GJ V( T Q,K'X|r>kޮtk8 ņCP8_>bV34K2Wżu,cx/%>e&Y}hK}2h*f3\;u ,FvYחޏ܄ ]ؤc_.$P(~\:C`M y3xZcE&Is`x Y@k`44mPAP>\+\B&n`&b<@p 0m|bN33ٲm'JПٱ 5Vf~HBRұEnlm,}ChKl[%_ax~BHdHayK2"Es]8vMF6^"^m{e`a,kw gw _U{!⸰os'~ÑߣϞnXA)**BӴXl\?nCeу/q\R_7*g%xF߭/8̻.`px7# d4vm4'nײ=,/e1fQJ8ܼ;ϑ)ZozK#ot1cy GEEՈTd2I*b$PJ^/'6n |k((^/HD8lA`~@&9@7oqA.uѰ8Xru|>Rp]T;n5o֌o??pr_D oa HɗWV1nUTVVR^^DQZZ[)))fĉC444,u76n|ؾ^oȦ^D>>&PV^y%477r}ukv~Q)\o_#"N%ɈH}˾&/n3O@Rٌ+J6x<.NhM6mĵGum^?Doo1mFa? A)K"ضM2$1~$Z[[xn<ȣ41 X,F6t]pREp\&NSVaƟ56nkcԕdYISĶmm[qX/"⺮R)xS:{;KVqcTFN|OwgwOqCB0eʔRlKo:{ς6/M{AdIENDB`socnetv-2.2/src/images/transposematrix.png000644 000765 000024 00000001161 13040701535 022027 0ustar00dimitrisstaff000000 000000 PNG  IHDR22?bKGD pHYs  tIME &}IIDATh+Q?ÒH6$U~lz%!l=C)a (c3%yuukj9s>sΝ{UE GG9"f*9?]9rH7u 8U8-Z]BP@g ; !~FcAbY Qi>,@b)C_w(|\cVP@xIs (Jk%rmV,BCM`sQPZ-:3{{ , aWBxl;PDbg_ dFH8Ϡs. IM+6 |7u ^Ť/5<$zc@:pS 8@z424Hkjwr{-'[ \#Ť]sY .d{C^VLn)B'ȴGw}&zztp"4cc^/Ӑ5yV>mldwS|UGnYCspС:!yGGLU~U;pkz:iPTDL}=*"i]lٚ-[ٓ'ݵ{l*+H {NM}׍nGrr8bɋ8 b^~+3je%"024ҙdAr2LNLl,thТ9Dz39IO1e2miR l]~.~"2:A)u5+RS+u3~gl6ټ xųe #@|E9?-"2Iⓚog:M;"2MR*PϢ!vF;;y]jBiӮ˗& |m3g()#u}>RF=Nn}e'=i_/ZgZI3oV׉ub4۹HE*Rn^kUŋ;yn @08LRJEI͟qlqZZr1iL3a2-˅SBs<Ϙpx,AcON҂W{z #vB3"BI GvƌWիiNہ( C%ŕy9O~o~gƍoRyy63u [TEJmu {),ٙe @fX8SzPa^DwS[J8yWgKlF]uS| ^"wj=0W-/7LN2மnS 7~_ "GZɶXol)),6m%ŋJKk} ; c40J=㏇CG_W?ѿc6!N'O< E<' sW4ʾ}쬩Dyp?">,Ą39ϷXF?.QJܷGx^D @@Ѯ:z{ʹ>MP KAKkk~mX8ҹJ}o`zzTyyZV4IENDB`socnetv-2.2/src/images/triadcensus.png000644 000765 000024 00000002204 13040701535 021107 0ustar00dimitrisstaff000000 000000 PNG  IHDR22?bKGD pHYs  tIME --2IDAThIhe'&mLRmFmm:8tPuV\ (.FPQtʅqU*pVp]hJժNմVkNir]~.Li{k?Oy^c8Nh%q9nzlL\Oϣ^!Fe`TZ؀6ɘV|w2+GfCSk g`dm/_4ո;ݛ Ÿ؈M7nAJhvS_@N&לXiP MUR2ߠ1 U+KVbu+'@vkb쉍K1+ЍqX/{42φ{J6 , vsـH9+'dsb)/d7af_ksG)dhkJĕ\ӪK=H¢AK rDqC|ێ;19$Ndݮ9*Gi‡* dͬ$uQZ!j?ЖA۱p6vDCȹp^p]N*Fq̼S4DΉ\ƥwd/J/wcKjhpzL6G6*8w@fF} #XtiSZw ȕx&6%v\:҅z"p S F{=@&vS2 >)b JG9+#dzȈ+>ip,35ROɆK70x=Úf?ݝS~T4?]%eH7|̦]X5ɞ\w}O}MNQjtBqm|[p07nsoȌp zvɔ'l1?c ٭{HgIENDB`socnetv-2.2/src/images/webcrawler.png000644 000765 000024 00000010052 13040701535 020720 0ustar00dimitrisstaff000000 000000 PNG  IHDR VόsRGBbKGD pHYs B(xtIME * &]  އe3:5IH+  (% ׾QTf oaEC,FF) "'$ DO7B g2ehtJgdA ,. +'#"   Vܟ#w&76'??%//  +,&%N%%I+.SDE):9#y ("  q wbDB,dIF* <9#(%   E .;i12!(*,)30"   ҍ  % 10        ,7&!+ E_ v  " \     e',c ('   ?1:-  ?3=?7!  %  +$    >1    &. H;!<- `  ) ))'$ ;#.>%N/0    9%R19 V1 , r 5 J C(  ' " 6 K_G(  .{Z     6  h܋^  <m~ z( Frʼ!޼(Z$2((" . ! <mnی') ȧ^#]Un% L+% ؈"rގ_ , & 3 o6-lIENDB`socnetv-2.2/src/images/zoomin.png000644 000765 000024 00000003164 13040701535 020104 0ustar00dimitrisstaff000000 000000 PNG  IHDR szz;IDATx[LSw4ےe[f&nn̖ EP-r\T* rFʴp -R(Jz@顥o=,@}_?''9Y դ_ `OuF^=N ͔JLH vdj[43S>8˼Qr0F~v)Kc6 _u -kZPH!)pj؜tсF'3^ę9fI/{ 쎮kdjy#G0VtHsOgqZ>fj9j,€  KɣUXR< w;oOWcNUcZ[%1\Dk}2琮KJ LZ, sB::(w=] ]D+kZ(O=2zS9&xUk Z,; ګکs13p Ze3(.Wr&쩍 bWVVYZQ?͸-GԢVχF3-9w' N~پZf/icDg7N<>,U+YpBף=/,Z,ωzg|y$#ε \ܱrl)%.9vQRX\xH44%ulً`BP.лP)~E؍;s x݉`Ys(d fWrV,k:Vhl86NBpDbw6ӊywcG#o"%iQ(9&ckszٹ> 34_k'̮tf|YrF{x!,>CE"N =1LĩY崺.7*kҰ=rkZgC^,HW@(_æA@>N.PpyUu%c/wyo#UDNvB(*.bJFwK!<"_!gC@6s]t9/6qwazfj\TI$땑p8vW n݄;Q)0>s9xG+>!_ A⥋A߅p24$mᑑHPPj0]L}~sqL.sI$|Dpڵku~A>z6{<ٳgS=BP(\Lq++sj2HxDvg(W+u }v?BBIENDB`socnetv-2.2/src/images/zoomout.png000644 000765 000024 00000003152 13040701535 020302 0ustar00dimitrisstaff000000 000000 PNG  IHDR szz1IDATxYP[di&i_8dRwx^@, d(50`b Lj c0$bAhG,h_{%j&CL9{^EaX5E:;'NfsF6L[^תh 8qfiN"kEm[< ւu|\ӒYԯNZ[_;kByFSщ0[\َ w\qI h'hr rJ[tMlMtfVoիLeƌ&\7aE՘^PmxzzpA!EH\zC! lnɈirͳĤ>$z*ZO¥Ū{iNcԮquH*[x=>F.^vHt 6bƟjA?T}O^nqɊS\XjZB2%IżQ]cdZNwm>mZWT<Ҍw,funh={JV0'D{1Cinol_tN1  \!z*5u%t%P49(C? Ĕo"yu$(<2,]S5{d&whћ|U0 Էv=艾9Ŗ4 =}.9!V(* ϨDmn SZ;fGO[8u_ܳVU?iP`DpȦWs OE%~\ɨde#+mڮko;x@"/_)gߺz=oukUܥljsj RS(Tcc#z39ǥf<7x_{D8[9"X 3æ3\*tT&Ac7F,ezqj`qx[#)|'po` |>;VNC|-Z\4 Y{עbv>~椝9 jY?dGT#~GiubWṯ:J%w?k(Ttlqf\L8<#">:Aq @w).g[i%%;2?KKKZ8]# %uC ɉ[ݢQV68z((66;!12Ju l0 Qڮn>457Z%F;v\ɘcbODӎUQ\{44sJ+Ff}B2<!YY'hG?OEaSXPxKRtvwKJJ ^w=@R$t‰b Bm~oE$''*jraqBHMKAᒕ? O>%p/wN)**jry:3(\t::pp j;I;&//dzftz-ΝUpmI~P8uuu!F&#R {wMDAݧeIENDB`socnetv-2.2/src/forms/dialogclusteringhierarchical.ui000644 000765 000024 00000020724 13040701535 024202 0ustar00dimitrisstaff000000 000000 DialogClusteringHierarchical true 0 0 550 450 0 0 550 450 600 600 10 Hierarchical Clustering 0 0 400 210 <html><head/><body><p>Agglomerative hierarchical clustering builds a hierarchy of actor clusters, based on their tie/distance dissimilarity, starting with single elements and aggregating them into clusters.</p><p>It takes a matrix (adjacency or geodesic distances) and a distance metric between actors as input and constructs a pair-wise dissimilarity matrix. </p><p>Initially, each actor starts in its own cluster (Level 0). In each subsequent Level, the pair of clusters with minimum distance are merged into a larger cluster. Then, the distance between the new cluster and the old ones is computed, using the specified clustering method (i.e. single-linkage clustering). The process is repeated until all actors end up in the same cluster. </p><p>Select an input matrix, a distance/dissimilarity metric and a clustering method (criterion) for the hierarchical cluster analysis. </p></body></html> Qt::RichText true Input matrix: 200 0 Qt::StrongFocus <html><head/><body><p><span style=" font-weight:600;">Adjacency:</span> Use the active network's adjacency matrix as input to hierarchical clustering.</p><p><span style=" font-weight:600;">Distances:</span> Use the active network's geodedic distances matrix as input. </p></body></html> Distance/dissimilarity metric: 200 0 Qt::StrongFocus <html><head/><body><p>Supported distance metrics for hierarchical clustering:</p><p><span style=" font-weight:600;">Euclidean distance</span>: The square root of the sum of squared differences between tie/distance profiles.</p><p><span style=" font-weight:600;">Jaccard distance</span>: The Jaccard index J is the ratio of same ties/distances reported by each pair of actors to the total number of their ties. Does not count absent ties. The Jaccard distance is 1 - J</p><p><span style=" font-weight:600;">Hamming distance</span>: The number of ties/distances to other actors which differ between each pair of actors.</p><p><span style=" font-weight:600;">Manhattan distance</span>: The sum of absoulute differences between tie/distance profiles.<br/></p></body></html> Clustering method (criterion): 200 0 Qt::StrongFocus <html><head/><body><p>Supported linkage criteria for agglomerative hierarchical clustering: </p><p><span style=" font-weight:600;">Single-linkage (minimum)</span>: The distance between two clusters will be determined by a single element pair, namely those two elements (one in each cluster) that have the shortest distance between them. In each step, the clusters that have the shortest distance will be merged. </p><p><span style=" font-weight:600;">Complete-linkage (maximum)</span>: The distance between two clusters will be determined by any two elements (one in each cluster) that have the longest distance between them. </p><p><span style=" font-weight:600;">Average-linkage (UPGMA)</span>: The distance between two clusters A and B is equal to the average of distances between all pairs of elements in A and B. </p><p><br/></p></body></html> Enable to include matrix diagonal in calculations Include input matrix diagonal Enable to include matrix diagonal in calculations Print dendrogram (avoid in large nets) Qt::Vertical 20 40 10 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok socnetv-2.2/src/forms/dialogdatasetselect.ui000644 000765 000024 00000006173 13040701535 022313 0ustar00dimitrisstaff000000 000000 DialogDataSetSelect true 0 0 490 200 0 0 460 200 490 215 10 Famous SNA data sets 400 90 <html><head/><body><p>Automatically recreate and visualize known data sets of Social Network Analysis, such as Padgett's Florentine families, Zachary's Karate Club, Knoke's Bureaucracies, etc.</p><p>Select the data set you want to re-create from the list.</p></body></html> Qt::RichText true 300 0 Qt::StrongFocus <html><head/><body><p>Click to select a data set</p></body></html> Qt::Vertical 20 40 10 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok socnetv-2.2/src/forms/dialogdissimilarities.ui000644 000765 000024 00000015204 13040701535 022657 0ustar00dimitrisstaff000000 000000 DialogDissimilarities true 0 0 550 330 0 0 550 330 600 600 10 Tie profile dissimilarities 0 0 400 150 <html><head/><body><p>Compute a <span style=" font-weight:600;">dissimilarities matrix</span>, where each element (i,j) is the pair-wise distance / dissimilarity of actors i and j tie profiles to all other actors, according to a selected metric. </p><p>Select a distance metric. For example, the &quot;Euclidean distance&quot; is the square root of the sum of the squared differences of tie values that actors i and j have to other actors. Hover over &quot;Distance Metric&quot; select box for more info on each metric.</p><p>Also, specify where the &quot;variables&quot; are. For instance, select Rows to measure the outbound ties between all pairs of actors. Select Both to measure both inbound and outbound ties. </p></body></html> Qt::RichText true Distance metric: 200 0 Qt::StrongFocus <html><head/><body><p>Select a matching method. </p><p><span style=" font-weight:600;">Exact Matches</span>: Examines pairs of actors for exact tie or distance matches (present or absent) to other actors and returns a proportion to their overall ties. </p><p><span style=" font-weight:600;">Positive Matches (Jaccard/Co-citation)</span>: Looks for same ties/distances reported by both actors. Returns the ratio to the total number of ties reported.</p><p><span style=" font-weight:600;">Hamming distance</span>: Reports the number of ties/distances to other actors which differ between each pair of actors.</p><p><span style=" font-weight:600;">Cosine similarity</span>: Computes the pair-wise similarity of actors as the dot product of their tie/distance vectors divided by the magnitude of these vectors. <br/></p></body></html> Variables in: 200 0 Qt::StrongFocus <html><head/><body><p><span style=" font-weight:600;">Rows: </span>Correlate outbound ties (or distances, depending on the selected matrix above) between pairs of actors.</p><p><span style=" font-weight:600;">Columns: </span>Correlate inbound ties (or distances, depending on the selected matrix above) between pairs of actors.</p><p><span style=" font-weight:600;">Both: </span> Correlate both outbound and inbound ties (or distances, depending on the selected matrix above) between pairs of actors.</p><p><br/></p></body></html> Enable to include matrix diagonal in calculations Include input matrix diagonal Qt::Vertical 20 20 10 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok socnetv-2.2/src/forms/dialogfilteredgesbyweight.ui000644 000765 000024 00000010006 13040701535 023514 0ustar00dimitrisstaff000000 000000 DialogFilterEdgesByWeight 0 0 400 210 400 210 10 Filter edges Sans Serif With this temporary action, you will make invisible some links according to their weight. Select a threshold then click on the desired radiobox below: Qt::RichText true 6 QLayout::SetDefaultConstraint 0 0 Sans Serif Weight Threshold Sans Serif 10 1 -100.000000000000000 Sans Serif Filter edges with weight over threshold Sans Serif Filter edges with weight below threshold Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogFilterEdgesByWeight accept() 248 254 157 274 buttonBox rejected() DialogFilterEdgesByWeight reject() 316 260 286 274 socnetv-2.2/src/forms/dialognodeedit.ui000644 000765 000024 00000025752 13040701535 021265 0ustar00dimitrisstaff000000 000000 DialogNodeEdit 0 0 464 280 450 280 500 300 DejaVu Sans 11 Node Properties Sans Serif 10 Node label Sans Serif 10 Sans Serif 10 Node size (default 8) DejaVu Sans 11 Qt::Horizontal 40 20 Sans Serif 10 8 false Sans Serif 10 Node value (disabled) DejaVu Sans 11 Qt::Horizontal 40 20 false Sans Serif 10 Sans Serif 10 Node color (click the button to select) DejaVu Sans 11 Qt::Horizontal 40 20 60 25 DejaVu Sans 11 ... 60 20 Sans Serif 10 Node shape DejaVu Sans 11 Qt::Horizontal 40 20 DejaVu Sans 11 :/images/box.png:/images/box.png DejaVu Sans 11 :/images/circle.png:/images/circle.png DejaVu Sans 11 :/images/diamond.png:/images/diamond.png DejaVu Sans 11 :/images/ellipse.png:/images/ellipse.png DejaVu Sans 11 :/images/triangle.png:/images/triangle.png DejaVu Sans 11 <html><head/><body><p><span style=" font-weight:600;">Node shape: Triangle</span></p><p>Change all node shapes to &quot;triangle&quot;.</p><p>This will apply to all existing nodes immediately.</p><p>Once you press OK, the &quot;triangle&quot; shape will be saved and it will be used in all future SocNetV sessions to create new nodes (except when loading network files which declare specific shapes for nodes).</p></body></html> :/images/star.png:/images/star.png Sans Serif 9 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogNodeEdit accept() 233 316 157 274 buttonBox rejected() DialogNodeEdit reject() 301 322 286 274 socnetv-2.2/src/forms/dialogranderdosrenyi.ui000644 000765 000024 00000046735 13040701535 022526 0ustar00dimitrisstaff000000 000000 DialogRandErdosRenyi 0 0 560 520 0 0 560 520 650 539 10 Erdős–Rényi network generator 0 0 540 85 16777215 96 Sans Serif QFrame::NoFrame QFrame::Sunken <html><head/><body><p>Generate random network according to Erdős–Rényi (ER) model. </p><p>In fact, there are two models: in <span style=" font-style:italic;">G(n,p)</span> edges are created with Bernoulli trials, while in <span style=" font-style:italic;">G(n,M) </span>a graph is randomly selected from all graphs with <span style=" font-style:italic;">n</span> nodes and <span style=" font-style:italic;">M</span> edges. Read more in the manual.</p></body></html> Qt::RichText true Qt::Horizontal 0 0 350 0 Sans Serif <html><head/><body><p>Nodes <span style=" font-style:italic;">n</span></p></body></html> 0 0 120 0 Sans Serif 0 9999 100 Qt::Horizontal 0 0 350 0 Sans Serif Model 120 0 Sans Serif <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Ubuntu'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This will create a new random network using <span style=" font-weight:600;">G(n,p)</span> model, where</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">n</span> is the number of nodes in the final graph</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">p</span> is the probability with which an edge is included in the graph</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If you select this model, you must enter the number of nodes n and the edge probability p. </p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">You may also select if the final network will be undirected or directed and if you want to allow nodes to link to themselves (diagonal non zero).</p></body></html> G(n, p) true false 100 0 Sans Serif <html><head/><body><p>This will create a new random network using <span style=" font-weight:600;">G(n,M)</span> model, where</p><p><span style=" font-weight:600;"> n</span> is the number of nodes in the final graph</p><p><span style=" font-weight:600;"> M</span> is the number of edges in the final graph</p><p>If you select this model, you must enter both the number of nodes n and the number of edges M</p><p>You may also select if the final network will be undirected or directed and if you want to allow nodes to link to themselves (diagonal non zero).</p></body></html> G(n, M) false false Qt::Horizontal QLayout::SetMinimumSize 0 0 350 0 Sans Serif <html><head/><body><p>Edges <span style=" font-style:italic;">M </span><span style=" color:#7c7c7c;">for G(n,M) model only</span></p></body></html> 120 0 Sans Serif 10 9999 Qt::Horizontal 0 0 350 0 Sans Serif <html><head/><body><p>Edge Probability <span style=" color:#7c7c7c;">applicable only in G(n,p) model</span></p></body></html> 0 0 120 0 Sans Serif 0.010000000000000 1.000000000000000 0.010000000000000 0.100000000000000 Qt::Horizontal 0 0 350 0 Sans Serif Graph Mode 0 0 120 0 Sans Serif Undirected false false 0 0 120 0 Sans Serif Directed false false Qt::Horizontal 0 0 350 0 Sans Serif Allow diagonals (loops) or set to zero? 0 0 120 0 Sans Serif Yes, allow false Qt::Horizontal Sans Serif 10 50 false Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogRandErdosRenyi accept() 257 373 157 274 buttonBox rejected() DialogRandErdosRenyi reject() 325 373 286 274 socnetv-2.2/src/forms/dialograndregular.ui000644 000765 000024 00000036754 13040701535 022004 0ustar00dimitrisstaff000000 000000 DialogRandRegular 0 0 570 440 0 0 570 440 580 480 10 d-Regular network generator Sans Serif 10 PreferDefault Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok Qt::Horizontal QLayout::SetMinimumSize 0 0 380 0 Sans Serif <html><head/><body><p>Degree <span style=" font-style:italic;">d</span></p></body></html> 110 0 Sans Serif <html><head/><body><p>Enter the degree <span style=" font-style:italic;">d</span> each new node will have.</p><p>Note that for a <span style=" font-style:italic;">d</span>-regular graph of <span style=" font-style:italic;">n</span> nodes, it is necessary that <span style=" font-style:italic;">n &gt;= d + 1 </span>and <span style=" font-style:italic;">n*d </span>is even.</p><p>In directed Graph Mode, this Degree <span style=" font-style:italic;">d</span> option corresponds to the outDegree and the inDegree, which will be both equal to <span style=" font-style:italic;">d</span> in the generated directed regular network. </p><p><br/></p></body></html> 2 9999 1 2 Qt::Horizontal Qt::Horizontal 0 0 380 0 Sans Serif <html><head/><body><p>The number n of nodes in the new Regular Network.</p><p>Note: For a <span style=" font-style:italic;">d</span>-regular graph of <span style=" font-style:italic;">n</span> nodes, it is necessary that <span style=" font-style:italic;">n &gt;= d + 1 </span>and <span style=" font-style:italic;">n*d </span>is even</p></body></html> Nodes 0 0 110 0 Sans Serif <html><head/><body><p>Enter number n of nodes in the new Regular Network.</p><p>For a <span style=" font-style:italic;">d</span>-regular graph of <span style=" font-style:italic;">n</span> nodes, it is necessary that <span style=" font-style:italic;">n &gt;= d + 1 </span>and <span style=" font-style:italic;">n*d </span>is even</p></body></html> 4 9999 100 Qt::Horizontal Qt::Horizontal 0 0 400 0 Sans Serif Allow diagonals (loops) or set to zero? 0 0 110 0 Sans Serif Check to allow loops (nodes linking to themselves) in the new network No loops false 0 0 400 0 Sans Serif Graph Mode 0 0 110 0 Sans Serif <html><head/><body><p>Click &quot;undirected&quot; to generate an undirected <span style=" font-style:italic;">d</span>-regular network </p></body></html> Undirected true false 0 0 100 0 Sans Serif <html><head/><body><p>Click &quot;directed&quot; to generate a directed <span style=" font-style:italic;">d</span>-regular network. </p><p>In this case, the Degree <span style=" font-style:italic;">d</span> option above corresponds to the outDegree and the inDegree which will be both equal to <span style=" font-style:italic;">d</span> in the generated regular network. </p><p>For instance, if you select <span style=" font-style:italic;">d</span>=4 and <span style=" font-style:italic;">Graph Mode</span>=directed, then each new node will have outDegree=4 (four outbound edges) and inDegree=4 (four inbound edges). The inbound and outbound edges of each node will not necessarily be from / to the same nodes.</p></body></html> Directed false 0 0 450 70 16777215 95 Sans Serif QFrame::NoFrame QFrame::Sunken <html><head/><body><p>Generate a <span style=" font-style:italic;">d-regular</span> random network of <span style=" font-style:italic;">n</span> nodes. This is a graph where each vertex has the same number of neighbors <span style=" font-style:italic;">d</span>.</p><p>This model produces undirect and directed provided that <span style=" font-style:italic;">n &gt;= d + 1 </span>and <span style=" font-style:italic;">nd </span>is even. Read more in the manual.</p></body></html> Qt::RichText true buttonBox accepted() DialogRandRegular accept() 257 373 157 274 buttonBox rejected() DialogRandRegular reject() 325 373 286 274 socnetv-2.2/src/forms/dialograndscalefree.ui000644 000765 000024 00000045124 13040701535 022263 0ustar00dimitrisstaff000000 000000 DialogRandScaleFree 0 0 580 530 0 0 580 530 600 580 10 Scale-free random network generator 0 0 450 100 16777215 110 Sans Serif QFrame::NoFrame QFrame::Sunken <html><head/><body><p>Generate a random scale-free network of <span style=" font-style:italic;">n</span> nodes according to the Barabási–Albert (BA) model which uses a preferential attachment mechanism. </p><p>The model starts with <span style=" font-style:italic;">m</span><span style=" font-style:italic; vertical-align:sub;">0</span> connected nodes. In each step a new node is added, along with <span style=" font-style:italic;">m</span> edges to existing nodes. Read more in the manual.</p></body></html> Qt::RichText true Qt::Horizontal 0 0 380 0 Sans Serif <html><head/><body><p>Nodes <span style=" font-style:italic;">n</span></p></body></html> 0 0 120 0 Sans Serif <html><head/><body><p>The amount of nodes in the resulting scale-free graph</p></body></html> 4 9999 100 Qt::Horizontal QLayout::SetMinimumSize 0 0 380 0 Sans Serif <html><head/><body><p>Power of preferential attachment <span style=" font-style:italic;">p</span></p></body></html> 120 0 Sans Serif <html><head/><body><p>The power p of preferential attachment </p><p>Leave 1 for linear preferential attachment (BA model).</p></body></html> 1 9999 1 1 Qt::Horizontal QLayout::SetMinimumSize 0 0 380 0 Sans Serif <html><head/><body><p>Initial connected nodes <span style=" font-style:italic;">m</span><span style=" font-style:italic; vertical-align:sub;">0</span></p></body></html> 120 0 Sans Serif <html><head/><body><p>The number m<span style=" vertical-align:sub;">0</span> of nodes in the initial connected network.</p><p>Leave 1 to start with just one node.</p></body></html> 1 9999 1 1 Qt::Horizontal QLayout::SetMinimumSize 0 0 380 0 Sans Serif <html><head/><body><p>Edges to add in each step <span style=" font-style:italic;">m</span></p></body></html> 120 0 Sans Serif <html><head/><body><p>Tthe number of edges to add in each step</p></body></html> 1 9999 1 2 Qt::Horizontal QLayout::SetMinimumSize 0 0 380 0 Sans Serif <html><head/><body><p>Zero appeal <span style=" font-style:italic;">α</span></p></body></html> 120 0 Sans Serif <html><head/><body><p>The initial attractiveness of a node - useful for isolate nodes with d =0</p></body></html> 0 9999 1 1 Qt::Horizontal 0 0 400 0 Sans Serif Graph Mode 0 0 120 0 Sans Serif Undirected true false 0 0 120 0 Sans Serif Directed false Qt::Horizontal 0 0 440 0 Sans Serif Allow diagonals (loops) or set to zero? 0 0 120 0 Sans Serif Check to allow loops (nodes linking to themselves) in the new network No loops false 10 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogRandScaleFree accept() 257 373 157 274 buttonBox rejected() DialogRandScaleFree reject() 325 373 286 274 socnetv-2.2/src/forms/dialograndsmallworld.ui000644 000765 000024 00000034472 13040701535 022516 0ustar00dimitrisstaff000000 000000 DialogRandSmallWorld 0 0 570 420 0 0 570 400 580 420 10 Small-World network generator 0 0 450 70 16777215 75 Sans Serif QFrame::NoFrame QFrame::Sunken <html><head/><body><p>Generate random network according to the <span style=" font-style:italic;">Watts &amp; Strogatz</span> model.</p><p>This model produces graphs with small-world properties, including short average path lengths and high clustering. Read more in the manual.</p></body></html> Qt::RichText true Qt::Horizontal 0 0 380 0 Sans Serif Nodes 0 0 110 0 Sans Serif <html><head/><body><p>The resulting graph will have N nodes and N*d/2 edges</p></body></html> 4 9999 100 Qt::Horizontal QLayout::SetMinimumSize 0 0 380 0 Sans Serif <html><head/><body><p>Mean Degree <span style=" font-style:italic;">d</span></p></body></html> 110 0 Sans Serif <html><head/><body><p>This is the mean edge degree each new node will have</p></body></html> 2 9999 2 10 Qt::Horizontal 0 0 400 0 Sans Serif <html><head/><body><p>Rewiring Probability <span style=" font-style:italic;">β</span></p></body></html> 0 0 110 0 Sans Serif 0.010000000000000 1.000000000000000 0.010000000000000 0.300000000000000 Qt::Horizontal 0 0 400 0 Sans Serif Graph Mode 0 0 110 0 Sans Serif Undirected true false 0 0 100 0 Sans Serif Directed false Qt::Horizontal 0 0 400 0 Sans Serif Allow diagonals (loops) or set to zero? 0 0 110 0 Sans Serif Check to allow loops (nodes linking to themselves) in the new network No loops false Qt::Horizontal Sans Serif 10 PreferDefault Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() DialogRandSmallWorld accept() 257 373 157 274 buttonBox rejected() DialogRandSmallWorld reject() 325 373 286 274 socnetv-2.2/src/forms/dialogsettings.ui000644 000765 000024 00000273442 13040701535 021333 0ustar00dimitrisstaff000000 000000 DialogSettings 0 0 469 613 Settings & Preferences Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok 0 General Data Export <html><head/><body><p><span style=" font-weight:600;">Default Save / Export folder </span></p><p>Click the small button on the far right corner to select a folder, where all SocNetV files and reports will be saved by default. </p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p></body></html> Save folder Qt::Horizontal 48 20 220 0 <html><head/><body><p><span style=" font-weight:600;">Default Save / Export folder </span></p><p>This is the path of the folder where all SocNetV files and reports will be saved. </p><p>Click the button on the right to select a new folder. </p><p>If the folder does not exist, it wil be created.</p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Default Save / Export folder </span></p><p>This is the path of the folder where all SocNetV files and reports will be saved. </p><p>Click the button on the right to select a new folder. </p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. If the folder does not exist, it wil be created.</p></body></html> PointingHandCursor <html><head/><body><p><span style=" font-weight:600;">Default Save / Export folder </span></p><p>Click this button to select a folder, where all SocNetV files and reports will be saved by default. </p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Default Save / Export folder </span></p><p>Click this button to select a folder, where all SocNetV files and reports will be saved by default. </p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. If the folder does not exist, it wil be created.</p></body></html> ... Debugging and Progressing <html><head/><body><p><span style=" font-weight:600;">Debug Messages</span></p><p>Enable or disable debug messages to strerr. </p><p>Once you enable this and press OK, you must close and run SocNetV again from the command line / terminal, in order to see the debug messages.</p><p>Enabling has a significant cpu cost but lets you know what SocNetV is actually doing.</p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Debug Messages</span></p><p>Enables or disable debug messages to strerr. </p><p>Enabling has a significant cpu cost but lets you know what SocNetV is actually doing.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> Qt::LeftToRight Print debug messages to command line <html><head/><body><p><span style=" font-weight:600;">Progress Bars</span></p><p>Enable or disable the application Progress Bars.</p><p>Progress Bars may appear during time-cost operations. </p><p>Enabling Progress Bars has a significant cpu cost but lets you know about the progress of a given operation.</p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Progress Bars</span></p><p>Enable or disable the application Progress Bars.</p><p>Progress Bars may appear during time-cost operations. </p><p>Enabling Progress Bars has a significant cpu cost but lets you know about the progress of a given operation.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> Qt::LeftToRight Show progress bars true Canvas options Background color DejaVu Sans 11 Qt::Horizontal 40 20 60 25 DejaVu Sans 11 PointingHandCursor <html><head/><body><p><span style=" font-weight:600;">Background color</span></p><p>Click this button to select a new background color. </p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Background color</span></p><p>Click this button to select a new background color. </p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> ... 60 20 Background Image Qt::Horizontal 40 20 200 0 <html><head/><body><p><span style=" font-weight:600;">Background image</span></p><p>This is the path of the default background image. </p><p>Click the button on the right to select a new background image.</p><p>If the file does not exist, the default background color will be used.</p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p><p><br/></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Background image</span></p><p>This is the path of the default background image. </p><p>Click the button on the right to select a new background image.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. If the file does not exist, the default background color will be used.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Background image</span></p><p>Click this button to select a new background image.</p><p>If the file does not exist, the default background color will be used.</p><p>This is a permanent setting. Once you press OK, it will be saved and will be the default of the application every time you run SocNetV - until you change it again.</p><p><br/></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Background image</span></p><p>Click this button to select a new background image.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. If the file does not exist, the default background color will be used.</p></body></html> ... <html><head/><body><p><span style=" font-weight:600;">Anti-Aliasing</span></p><p>Enable or disable Anti-Aliasing.</p><p>Anti-aliasing is a technique which makes nodes, lines and text, smoother and fancier. But it comes at the cost of speed... </p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p><p><br/></p></body></html> <html><head/><body><p><span style=" font-weight:600;">Anti-Aliasing</span></p><p>Enable or disable Anti-Aliasing.</p><p>Anti-aliasing is a technique which makes nodes, lines and text, smoother and fancier. But it comes at the cost of speed... </p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p><p><br/></p></body></html> Qt::LeftToRight Enable antialiasing <html><head/><body><p><span style=" font-weight:600;">SocNetV logo on exported images</span></p><p>Enable or disable the printing of a small SocNetV logo on exported PNG and BMP network images </p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> <html><head/><body><p><span style=" font-weight:600;">SocNetV logo on exported images</span></p><p>Enable or disable the printing of a small SocNetV logo on exported PNG and BMP network images </p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> Qt::LeftToRight Print SocNetV logo on exported Images true Window options <html><head/><body><p><span style=" font-weight:600;">Control panel</span></p><p>Enable or disable the Control panel (left panel)</p><p>The Control Panel is the widget at the left of the application window, where you can find essential functions and options for Editing, Analyzing and Visualizing your network data.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> <html><head/><body><p><span style=" font-weight:600;">Control panel</span></p><p>Enable or disable the Control panel (left panel)</p><p>The Control Panel is the widget at the left of the application window, where you can find essential functions and options for Editing, Analyzing and Visualizing your network data.</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> Qt::LeftToRight Show Control panel (left panel) true <html><head/><body><p><span style=" font-weight:600;">Statistics panel</span></p><p>Enable or disable the statistics panel (right panel)</p><p>The Statistics panel is the widget at the right of the application window, where you can see statistics about the whole network such as node and edge/arc count, density etc as well as statistics about the last clicked node (in-degree, out-degree, clustering coefficient etc).</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> <html><head/><body><p><span style=" font-weight:600;">Statistics panel</span></p><p>Enable or disable the statistics panel (right panel)</p><p>The Statistics panel is the widget at the right of the application window, where you can see statistics about the whole network such as node and edge/arc count, density etc as well as statistics about the last clicked node (in-degree, out-degree, clustering coefficient etc).</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> Qt::LeftToRight Show Statistics panel (right panel) true <html><head/><body><p><span style=" font-weight:600;">Toolbar</span></p><p>Enable or disable the application toolbar.</p><p>The toolbar is the widget right below the menu, and carries useful icons. You can disable it if you like from here...</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> <html><head/><body><p><span style=" font-weight:600;">Toolbar</span></p><p>Enable or disable the application toolbar.</p><p>The toolbar is the widget right below the menu, and carries useful icons. You can disable it if you like from here...</p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> Qt::LeftToRight Show toolbar true <html><head/><body><p><span style=" font-weight:600;">Statusbar</span></p><p>Enable or disable the application statusbar.</p><p>The statusbar is the widget at the bottom of the window, where messages appear. </p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> <html><head/><body><p><span style=" font-weight:600;">Statusbar</span></p><p>Enable or disable the application statusbar.</p><p>The statusbar is the widget at the bottom of the window, where messages appear. </p><p>This is a permanent setting, it will be the default of the application every time you run SocNetV. </p></body></html> Qt::LeftToRight Show statusbar true Nodes <html><head/><body><p><span style=" font-weight:600;">Default node settings</span></p><p>Any change to these settings will apply to all existing nodes immediately.</p><p>Once you press OK, these settings will be saved and they will be used in all future SocNetV sessions.</p></body></html> Node settings <html><head/><body><p><span style=" font-weight:600;">Node color</span></p><p>Click the colored button to select a new color for all nodes.</p><p>Any change to this setting will apply to all existing nodes immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node color when creating new nodes (except when loading network files which declare specific node settings).</p></body></html> Node color DejaVu Sans 11 Qt::Horizontal 40 20 60 25 DejaVu Sans 11 PointingHandCursor <html><head/><body><p><span style=" font-weight:600;">Node color</span></p><p>Click this button to select a new node color for all nodes.</p><p>Any change to this setting will apply to all existing nodes immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node color when creating new nodes (except when loading network files which declare specific node settings).</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Node color</span></p><p>Click to select a new default node color.</p><p>Any change to this setting will apply to all existing nodes immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node color.</p></body></html> ... 60 20 <html><head/><body><p><span style=" font-weight:600;">Node shape</span></p><p>Click on any of the following shapes to select a new node shape for all nodes. </p><p>This will apply to all existing nodes immediately.</p><p>Once you press OK, the default node shape will be saved and it will be used in all future SocNetV sessions when creating new nodes (except when loading network files which declare specific shapes for nodes).</p></body></html> Node shape DejaVu Sans 11 Qt::Horizontal 40 20 DejaVu Sans 11 <html><head/><body><p><span style=" font-weight:600;">Node shape: Square</span></p><p>Change all node shapes to &quot;square&quot; or &quot;box&quot;.</p><p>This will apply to all existing nodes immediately.</p><p>Once you press OK, the &quot;box&quot; shape will be saved and it will be used in all future SocNetV sessions to create new nodes (except when loading network files which declare specific shapes for nodes).</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Default node shape</span></p><p>Click on any shape to select a new node shape for all nodes. This will apply to all existing nodes immediately.</p><p>Once you press OK, the default node shape will be saved and it will be used in all future SocNetV sessions.</p></body></html> :/images/box.png:/images/box.png DejaVu Sans 11 <html><head/><body><p><span style=" font-weight:600;">Node shape: Circle</span></p><p>Change all node shapes to &quot;circle&quot;.</p><p>This will apply to all existing nodes immediately.</p><p>Once you press OK, the &quot;circle&quot; shape will be saved and it will be used in all future SocNetV sessions to create new nodes (except when loading network files which declare specific shapes for nodes).</p></body></html> :/images/circle.png:/images/circle.png DejaVu Sans 11 <html><head/><body><p><span style=" font-weight:600;">Node shape: Diamond</span></p><p>Change all node shapes to &quot;diamond&quot;.</p><p>This will apply to all existing nodes immediately.</p><p>Once you press OK, the &quot;diamond&quot; shape will be saved and it will be used in all future SocNetV sessions to create new nodes (except when loading network files which declare specific shapes for nodes).</p></body></html> :/images/diamond.png:/images/diamond.png DejaVu Sans 11 <html><head/><body><p><span style=" font-weight:600;">Node shape: Ellipse</span></p><p>Change all node shapes to &quot;ellipse&quot;.</p><p>This will apply to all existing nodes immediately.</p><p>Once you press OK, the &quot;ellipse&quot; shape will be saved and it will be used in all future SocNetV sessions when creating new nodes (except when loading network files which declare specific shapes for nodes).</p></body></html> :/images/ellipse.png:/images/ellipse.png DejaVu Sans 11 <html><head/><body><p><span style=" font-weight:600;">Node shape: Star</span></p><p>Change all node shapes to &quot;star&quot;.</p><p>This will apply to all existing nodes immediately.</p><p>Once you press OK, the &quot;star&quot; shape will be saved and it will be used in all future SocNetV sessions to create new nodes (except when loading network files which declare specific shapes for nodes).</p></body></html> :/images/triangle.png:/images/triangle.png DejaVu Sans 11 <html><head/><body><p><span style=" font-weight:600;">Node shape: Triangle</span></p><p>Change all node shapes to &quot;triangle&quot;.</p><p>This will apply to all existing nodes immediately.</p><p>Once you press OK, the &quot;triangle&quot; shape will be saved and it will be used in all future SocNetV sessions to create new nodes (except when loading network files which declare specific shapes for nodes).</p></body></html> :/images/star.png:/images/star.png <html><head/><body><p><span style=" font-weight:600;">Node size</span></p><p>Use the spin box to select a new size (in pixels) for all nodes. </p><p>Actual node size will vary according to selected shape. For instance, if the selected shape is circle and the selected size is 8, then the circle will have a diameter of 16 pixels. </p><p>Any change to this setting will apply to all existing nodes immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node size when creating new nodes (except when loading network files which declare specific node settings).</p></body></html> Node size DejaVu Sans 11 Qt::Horizontal 40 20 DejaVu Sans 11 SizeVerCursor <html><head/><body><p><span style=" font-weight:600;">Node size</span></p><p>Select a new size for all nodes. Actual node size will vary according to selected shape. For instance, if the selected shape is circle and the size is 8, then the circle will have a diameter of 16 pixels. </p><p>Any change to this setting will apply to all existing nodes immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node size.</p></body></html> 1 8 Node Number settings <html><head/><body><p><span style=" font-weight:600;">Node number color</span></p><p>Change the color for all node numbers. Click the colored button on the right corner to select a new color.</p><p>Any change to this setting will apply to all existing node numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node number color when creating new nodes.</p></body></html> Number color DejaVu Sans 11 Qt::Horizontal 40 20 60 25 DejaVu Sans 11 PointingHandCursor <html><head/><body><p><span style=" font-weight:600;">Node number color</span></p><p>Click to select a new color for all node numbers.</p><p>Any change to this setting will apply to all existing node numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node number color when creating new nodes.</p></body></html> ... 60 20 <html><head/><body><p><span style=" font-weight:600;">Number distance from node</span></p><p>Change the distance of each number from the respective node. Use the spin box on the right to select a new distance (in pixels).</p><p>Any change to this setting will apply to all existing node numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default distance from node.</p></body></html> Number distance DejaVu Sans 11 Qt::Horizontal 40 20 DejaVu Sans 11 SizeVerCursor <html><head/><body><p><span style=" font-weight:600;">Number distance from nodes</span></p><p>Select a new distance (in pixels) of numbers from the respective nodes.</p><p>Any change to this setting will apply to all existing node numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default distance from node.</p></body></html> 1 8 <html><head/><body><p><span style=" font-weight:600;">Node numbers</span></p><p>Enable or disable displaying node numbers.</p><p>Any change will apply to all existing nodes immediately.</p><p>Once you press OK, this setting will be saved and it will be used in all future SocNetV sessions.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Node numbers</span></p><p>Enable or disable displaying node numbers.</p><p>Any change will apply to all existing nodes immediately.</p><p>Once you press OK, this setting will be saved and it will be used in all future SocNetV sessions.</p></body></html> Qt::LeftToRight Display node numbers <html><head/><body><p><span style=" font-weight:600;">Node number font size</span></p><p>Change the font size (in pixels) of all node numbers. Use the spin box on the right to select a new font size (in pixels).</p><p>Any change to this setting will apply to all existing node numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node number size.</p></body></html> Number font size DejaVu Sans 11 Qt::Horizontal 40 20 DejaVu Sans 11 SizeVerCursor <html><head/><body><p><span style=" font-weight:600;">Node number size</span></p><p>Select a new font size (in pixels) for node numbers. Set it to 0 to let the program automagically select a different font size according to the node size.</p><p>Any change to this setting will apply to all existing node numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node number size.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Node number size</span></p><p>Select a new font size (in pixels) for node numbers. Set it to 0 to let the program automagically select a different font size according to the node size.</p><p>Any change to this setting will apply to all existing node numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node number size.</p></body></html> 8 <html><head/><body><p><span style=" font-weight:600;">Node numbers inside shapes</span></p><p>Enable or disable displaying node numbers inside the node shapes</p><p>Any change will apply to all existing nodes immediately.</p><p>Once you press OK, this setting will be saved and it will be used in all future SocNetV sessions.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Node numbers inside shapes</span></p><p>Enable or disable displaying node numbers inside the node shapes</p><p>Any change will apply to all existing nodes immediately.</p><p>Once you press OK, this setting will be saved and it will be used in all future SocNetV sessions.</p></body></html> Qt::LeftToRight Display node numbers inside node shapes Node Label settings <html><head/><body><p><span style=" font-weight:600;">Node labels</span></p><p>Enable or disable displaying labels below the nodes.</p><p>Any change will apply to all existing nodes immediately.</p><p>Once you press OK, this setting will be saved and it will be used in all future SocNetV sessions.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Node labels</span></p><p>Enable or disable displaying labels below the nodes.</p><p>Any change will apply to all existing nodes immediately.</p><p>Once you press OK, this setting will be saved and it will be used in all future SocNetV sessions.</p></body></html> Qt::LeftToRight Display node labels Label color DejaVu Sans 11 Qt::Horizontal 40 20 60 25 DejaVu Sans 11 PointingHandCursor <html><head/><body><p><span style=" font-weight:600;">Node label color</span></p><p>Click to select a new color for node labels.</p><p>Any change to this setting will apply to all existing node labels immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node label color.</p></body></html> ... 60 20 Label font size DejaVu Sans 11 Qt::Horizontal 40 20 DejaVu Sans 11 SizeVerCursor <html><head/><body><p><span style=" font-weight:600;">Node label size</span></p><p>Select a new font size (in pixels) for all node labels.</p><p>Any change to this setting will apply to all existing node labels immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default node label size.</p></body></html> 1 8 Label distance DejaVu Sans 11 Qt::Horizontal 40 20 DejaVu Sans 11 SizeVerCursor <html><head/><body><p><span style=" font-weight:600;">Label distance from node</span></p><p>Change the distance of labels from the respective nodes.</p><p>Any change to this setting will apply to all existing node labels immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default label distance from node.</p></body></html> 8 groupBox_3 groupBox_4 groupBox_7 Edges Edge settings <html><head/><body><p><span style=" font-weight:600;">Edges </span></p><p>Enable or disable displaying edges.</p><p>Any change will apply to all existing edges immediately.</p><p><br/></p></body></html> Qt::LeftToRight Display edges <html><head/><body><p><span style=" font-weight:600;">Edges </span></p><p>Enable or disable displaying arrows in directed edges. Useful If you work with directional social network data. You can disable it if your network is undirected.</p><p>Any change will apply to all existing edges immediately and saved for all future sessions (until you change it again).</p><p><br/></p></body></html> Qt::LeftToRight Display edge arrows <html><head/><body><p><span style=" font-weight:600;">Edge color</span></p><p>Click the colored button on the right to select a new color for all edges.</p><p>Any change to this setting will apply to all existing edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge color. Until you change it again.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Edge color</span></p><p>Click the colored button on the right to select a new color for all edges.</p><p>Any change to this setting will apply to all existing edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge color. Until you change it again.</p></body></html> Edge color DejaVu Sans 11 Qt::Horizontal 40 20 60 25 DejaVu Sans 11 PointingHandCursor <html><head/><body><p><span style=" font-weight:600;">Edge color</span></p><p>Click to select a new color for all edges.</p><p>Any change to this setting will apply to all existing edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge color. Until you change it again.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Edge color</span></p><p>Click to select a new color for all edges.</p><p>Any change to this setting will apply to all existing edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge color. Until you change it again.</p></body></html> ... 60 20 <html><head/><body><p><span style=" font-weight:600;">Negative edge color</span></p><p>Click the colored button at the right corner to select a new color for negative weighted edges.</p><p>Any change to this setting will apply to all existing &quot;negative&quot; edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge color for &quot;negative&quot; edges. Until you change it again.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Negative edge color</span></p><p>Click the colored button at the right corner to select a new color for negative weighted edges.</p><p>Any change to this setting will apply to all existing &quot;negative&quot; edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge color for &quot;negative&quot; edges. Until you change it again.</p></body></html> Edge color (negative weights) DejaVu Sans 11 Qt::Horizontal 40 20 60 25 DejaVu Sans 11 PointingHandCursor <html><head/><body><p><span style=" font-weight:600;">Negative edge color</span></p><p>Click this button to select a new default edge color for all negative weighted edges.</p><p>Any change to this setting will apply to all existing &quot;negative&quot; edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default color for &quot;negative&quot; edges. Until you change it again.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Negative edge color</span></p><p>Click this button to select a new default edge color for all negative weighted edges.</p><p>Any change to this setting will apply to all existing &quot;negative&quot; edges immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default color for &quot;negative&quot; edges. Until you change it again.</p></body></html> ... 60 20 <html><head/><body><p><span style=" font-weight:600;">Default edge shape</span></p><p>Select the default edge shape for all edges. </p><p>Edges can be either straight lines (default) or bezier curves.</p><p>This will apply to all existing edgesimmediately.</p><p>Once you press OK, the default edge shape will be saved and it will be used in all future SocNetV sessions.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Default edge shape</span></p><p>Select the default edge shape for all edges. </p><p>Edges can be either straight lines (default) or bezier curves.</p><p>This will apply to all existing edgesimmediately.</p><p>Once you press OK, the default edge shape will be saved and it will be used in all future SocNetV sessions.</p></body></html> Edge shape DejaVu Sans 11 Qt::Horizontal 40 20 <html><head/><body><p><span style=" font-weight:600;">Default edge shape</span></p><p>Select the default edge shape for all edges. </p><p>Edges can be either straight lines (default) or bezier curves.</p><p>This will apply to all existing edgesimmediately.</p><p>Once you press OK, the default edge shape will be saved and it will be used in all future SocNetV sessions.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Default edge shape</span></p><p>Select the default edge shape for all edges. </p><p>Edges can be either straight lines (default) or bezier curves.</p><p>This will apply to all existing edgesimmediately.</p><p>Once you press OK, the default edge shape will be saved and it will be used in all future SocNetV sessions.</p></body></html> Straight Lines false <html><head/><body><p><span style=" font-weight:600;">Default edge shape</span></p><p>Select the default edge shape for all edges. </p><p>Edges can be either straight lines (default) or bezier curves.</p><p>This will apply to all existing edgesimmediately.</p><p>Once you press OK, the default edge shape will be saved and it will be used in all future SocNetV sessions.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Default edge shape</span></p><p>Select the default edge shape for all edges. </p><p>Edges can be either straight lines (default) or bezier curves.</p><p>This will apply to all existing edgesimmediately.</p><p>Once you press OK, the default edge shape will be saved and it will be used in all future SocNetV sessions.</p></body></html> Bezier Curves Weight number settings <html><head/><body><p><span style=" font-weight:600;">Edge weight numbers</span></p><p>Enable or disable edge weight numbers. When enabled, a number will be displayed along every edge indicating the edge weight.</p><p>Any change to this setting will apply to all existing weight numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default when creating new edges.</p></body></html> Qt::LeftToRight Display edge weight numbers Weight number color DejaVu Sans 11 Qt::Horizontal 40 20 60 25 DejaVu Sans 11 PointingHandCursor <html><head/><body><p><span style=" font-weight:600;">Edge weight number color</span></p><p>Click to select a new color for all edge weight numbers.</p><p>Any change to this setting will apply to all existing weight numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge weight number color when creating new edges.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Edge weight number color</span></p><p>Click to select a new color for all edge weight numbers.</p><p>Any change to this setting will apply to all existing weight numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge weight number color when creating new edges.</p></body></html> ... 60 20 Weight number font size DejaVu Sans 11 Qt::Horizontal 40 20 DejaVu Sans 11 SizeVerCursor <html><head/><body><p><span style=" font-weight:600;">Edge weight number text size</span></p><p>Click to select a new text size (in pixels) for all edge weight numbers.</p><p>Any change to this setting will apply to all existing weight numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge weight number size when creating new edges.</p></body></html> <html><head/><body><p><span style=" font-weight:600;">Edge weight number text size</span></p><p>Click to select a new text size (in pixels) for all edge weight numbers.</p><p>Any change to this setting will apply to all existing weight numbers immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge weight number size when creating new edges.</p></body></html> 1 8 Edge label settings <html><head/><body><p><span style=" font-weight:600;">Edge labels</span></p><p>Enable or disable displaying edge labels.</p><p>Any change will apply to all existing edges immediately.</p><p>Once you press OK, this setting will be saved and it will be used in all future SocNetV sessions.</p></body></html> Qt::LeftToRight Display edge labels Label color DejaVu Sans 11 Qt::Horizontal 40 20 false 60 25 DejaVu Sans 11 PointingHandCursor <html><head/><body><p><span style=" font-weight:600;">Default edge label color</span></p><p>Click to select a new default color for edge labels.</p><p>Any change to this setting will apply to all existing edge labels immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge label color.</p></body></html> ... 60 20 Label font size DejaVu Sans 11 Qt::Horizontal 40 20 false DejaVu Sans 11 SizeVerCursor <html><head/><body><p><span style=" font-weight:600;">Default edge label size</span></p><p>Select the default font size for all edge labels.</p><p>Any change to this setting will apply to all existing edge labels immediately.</p><p>This is a permanent setting. Once you press OK, it will be saved and used in all future SocNetV sessions as default edge label size.</p></body></html> 8 buttonBox accepted() DialogSettings accept() 227 281 157 274 buttonBox rejected() DialogSettings reject() 295 287 286 274 socnetv-2.2/src/forms/dialogsimilaritymatches.ui000644 000765 000024 00000017575 13040701535 023231 0ustar00dimitrisstaff000000 000000 DialogSimilarityMatches true 0 0 550 360 0 0 550 360 600 600 10 Similarity: Matches 0 0 400 150 <html><head/><body><p>Compute a <span style=" font-weight:600;">similarity matrix</span>, where each element (i,j) is the pair-wise similarity score of actors i and j according to the selected &quot;matching&quot; method. For example, the &quot;Simple Matching&quot; method counts the number of times that actors i and j have the same tie / distance (present or absent) to other actors. </p><p>Select input matrix and where the &quot;variables&quot; are. For instance, select Adjacency matrix and Rows to correlate the outbound ties between all pairs of actors. Select Both to correlate the both inbound and outbound ties. Hover over &quot;Matching measure&quot; select box for more info on each method.</p></body></html> Qt::RichText true Input matrix: 200 0 Qt::StrongFocus <html><head/><body><p><span style=" font-weight:600;">Adjacency:</span> Use the active network's adjacency matrix as input to correlate ties between all pairs of actors. </p><p><span style=" font-weight:600;">Distances:</span> Use the active network's geodesic distances matrix to correlate distances between all pairs of actors.</p></body></html> Variables in: 200 0 Qt::StrongFocus <html><head/><body><p><span style=" font-weight:600;">Rows: </span>Correlate outbound ties (or distances, depending on the selected matrix above) between pairs of actors.</p><p><span style=" font-weight:600;">Columns: </span>Correlate inbound ties (or distances, depending on the selected matrix above) between pairs of actors.</p><p><span style=" font-weight:600;">Both: </span>Correlate both outbound and inbound ties (or distances, depending on the selected matrix above) between pairs of actors. In this case, the input matrix is expanded by listing row vectors followed by column vectors. This is useful only when you have directed data.</p><p><br/></p></body></html> Matching measure: 200 0 Qt::StrongFocus <html><head/><body><p>Select a matching method. </p><p><span style=" font-weight:600;">Exact Matches</span>: Examines pairs of actors for exact tie or distance matches (present or absent) to other actors and returns a proportion to their overall ties. </p><p><span style=" font-weight:600;">Positive Matches (Jaccard/Co-citation)</span>: Looks for same ties/distances reported by both actors. Returns the ratio to the total number of ties reported.</p><p><span style=" font-weight:600;">Hamming distance</span>: Reports the number of ties/distances to other actors which differ between each pair of actors.</p><p><span style=" font-weight:600;">Cosine similarity</span>: Computes the pair-wise similarity of actors as the dot product of their tie/distance vectors divided by the magnitude of these vectors. <br/></p></body></html> Enable to include matrix diagonal in calculations Include input matrix diagonal Qt::Vertical 20 20 10 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok socnetv-2.2/src/forms/dialogsimilaritypearson.ui000644 000765 000024 00000014107 13040701535 023240 0ustar00dimitrisstaff000000 000000 DialogSimilarityPearson true 0 0 550 300 0 0 550 300 600 600 10 Pearson Correlations 0 0 400 150 <html><head/><body><p>Compute <span style=" font-weight:600;">Pearson Product Moment Correlation Coefficients</span> (PPMCC) between the rows, the columns or both of a social network matrix (adjacency or distance matrix). The result is a <span style=" font-weight:600;">correlation matrix </span>containing the correlation coefficients between each variable (i.e. row) and the others. This might be useful if you want to check the pair-wise similarity of the actors, in terms of their ties. </p><p>Select input matrix and what &quot;variables&quot; to correlate. For instance, select Adjacency matrix and Rows to correlate the outbound ties between all pairs of actors. Select Both to correlate the both inbound and outbound ties.</p></body></html> Qt::RichText true Input matrix: 200 0 Qt::StrongFocus <html><head/><body><p><span style=" font-weight:600;">Adjacency:</span> Use the active network's adjacency matrix as input to correlate ties between all pairs of actors. </p><p><span style=" font-weight:600;">Distances:</span> Use the active network's geodesic distances matrix to correlate distances between all pairs of actors.</p></body></html> Variables in: 200 0 Qt::StrongFocus <html><head/><body><p><span style=" font-weight:600;">Rows: </span>Correlate outbound ties (or distances, depending on the selected matrix above) between pairs of actors.</p><p><span style=" font-weight:600;">Columns: </span>Correlate inbound ties (or distances, depending on the selected matrix above) between pairs of actors.</p><p><span style=" font-weight:600;">Both: </span>Correlate both outbound and inbound ties (or distances, depending on the selected matrix above) between pairs of actors. In this case, the input matrix is expanded by listing row vectors followed by column vectors. This is useful only when you have directed data.</p><p><br/></p></body></html> Enable to include matrix diagonal in calculations Include input matrix diagonal Qt::Vertical 20 40 10 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok socnetv-2.2/src/forms/edgeeditdialog.ui000644 000765 000024 00000022101 13040701535 021225 0ustar00dimitrisstaff000000 000000 EdgeEditDialog 0 0 450 280 450 280 Node Properties 12 Enter node label 12 false 12 Enter node value (disabled) 12 Qt::Horizontal 40 20 false 12 12 Select line shape 12 Qt::Horizontal 40 20 12 :/images/box.png:/images/box.png 12 :/images/circle.png:/images/circle.png 12 :/images/diamond.png:/images/diamond.png 12 :/images/ellipse.png:/images/ellipse.png 12 :/images/triangle.png:/images/triangle.png 12 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok 12 Select link color (click the button) 12 Qt::Horizontal 40 20 60 25 12 ... 60 20 12 Select the link weight (default 1) 12 Qt::Horizontal 40 20 12 8 buttonBox accepted() EdgeEditDialog accept() 233 316 157 274 buttonBox rejected() EdgeEditDialog reject() 301 322 286 274 socnetv-2.2/src/forms/webcrawlerdialog.ui000644 000765 000024 00000024075 13040701535 021624 0ustar00dimitrisstaff000000 000000 WebCrawlerDialog 0 0 518 362 0 0 500 350 650 600 10 Generate network from web links 0 0 500 130 600 500 false QFrame::NoFrame <html><head/><body><p>Use the built-in web crawler to scan the HTML code of a given initial URL (i.e. a website) and map all internal or external links to other pages found there. </p><p>As new urls are discovered, the crawler follows them to scan their HTML code for links as well. For more details, see the Manual. </p><p>Enter the initial URL below and change crawling parameters if you like.</p></body></html> Qt::RichText true Initial URL Qt::Horizontal 40 20 0 0 250 22 400 24 <html><head/><body><p>Enter the initial url/domain to start crawling from, i.e. http://www.iefimerida.gr </p><p>You may omit http:// if you want. </p></body></html> <html><head/><body><p>Set the total urls to be crawled. </p><p>This is the total nodes the result network will have. </p><p>Set value to 0, if you don't want any limits...</p></body></html> Total urls to crawl 11 PreferAntialias Qt::Horizontal 40 20 <html><head/><body><p>Set the total urls to be crawled. </p><p>This is the total nodes the result network will have. </p><p>Set value to 0, if you don't want any limits...</p></body></html> 1 2000 600 <html><head/><body><p>Set the max links inside a page to be followed and crawled by SocNetV.</p><p>Set this to zero if you don't want to have this limit. In this case SocNetV will follow and crawl every link found in a page.</p></body></html> Max links in each page to follow 11 PreferAntialias Qt::Horizontal 40 20 <html><head/><body><p>Set the max links inside a page to be followed and crawled by SocNetV.</p><p>Set this to zero if you don't want to have this limit. In this case SocNetV will follow and crawl every link found in a page.</p></body></html> <html><head/><body><p>If enabled, the crawler will map <span style=" font-weight:600;">external links</span>. </p><p>For instance, if you start crawling from www.iefimerida.gr and we find there a link to another domain, i.e. www.linuxinsider.gr, then we will go outside of iefimerida.gr and crawl linuxinsider.gr too. </p><p>If you don't want to crawl external links, disable this option. </p></body></html> Crawl external links true <html><head/><body><p>If enabled the crawler will scan and map <span style=" font-weight:600;">internal links </span>as well (i.e. pages within the initial website). </p><p><br/></p><p>If you do not want to crawl internal links disable this option. </p><p>Default is not to crawl internal links.</p></body></html> Crawl internal links Qt::Vertical 497 27 true Sans Serif 10 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() WebCrawlerDialog accept() 257 324 157 274 buttonBox rejected() WebCrawlerDialog reject() 325 324 286 274 socnetv-2.2/nets/10actors-colors.dot000644 000765 000024 00000000220 13040701535 020431 0ustar00dimitrisstaff000000 000000 digraph mydot { node [color=red, shape=box]; a -> b -> c ->d node [color=pink, shape=circle]; d->e->a->f->j->k->l->o [weight=1, color=gray]; } socnetv-2.2/nets/11actors-pajek-matrix.adj000644 000765 000024 00000002165 13040701535 021507 0ustar00dimitrisstaff000000 000000 *Vertices 11 1 "minister1" 0.2912 0.2004 ellipse 2 "pminister" 0.4875 0.0153 diamond 3 "minister2" 0.3537 0.3416 ellipse 3 "minister2" 0.3537 0.3416 ellipse 4 "minister3" 0.4225 0.5477 ellipse 5 "minister4" 0.4538 0.1603 ellipse 6 "minister5" 0.4900 0.3836 ellipse 7 "minister6" 0.6212 0.5038 ellipse 8 "minister7" 0.6450 0.2023 ellipse 9 "advisor1" 0.6488 0.6031 box 10 "advisor2" 0.3212 0.5515 box 11 "advisor3" 0.7188 0.4218 box *Matrix 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1 0 1 1 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 1 0 1 1 1 0 0 0 0 1 0 1 1 0 1 1 0 0 0 0 0 0 1 0 0 0 1 1 0 1 0 1 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 0 0 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0socnetv-2.2/nets/11actors-pajek-matrix.paj000644 000765 000024 00000002165 13040701535 021523 0ustar00dimitrisstaff000000 000000 *Vertices 11 1 "minister1" 0.2912 0.2004 ellipse 2 "pminister" 0.4875 0.0153 diamond 3 "minister2" 0.3537 0.3416 ellipse 4 "minister2" 0.3537 0.3416 ellipse 5 "minister3" 0.4225 0.5477 ellipse 6 "minister4" 0.4538 0.1603 ellipse 7 "minister5" 0.4900 0.3836 ellipse 8 "minister6" 0.6212 0.5038 ellipse 9 "minister7" 0.6450 0.2023 ellipse 10 "advisor1" 0.6488 0.6031 box 11 "advisor2" 0.3212 0.5515 box 12 "advisor3" 0.7188 0.4218 box *Matrix 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1 0 1 1 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 1 0 1 1 1 0 0 0 0 1 0 1 1 0 1 1 0 0 0 0 0 0 1 0 0 0 1 1 0 1 0 1 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 0 0 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0socnetv-2.2/nets/4-actors-sm.csv000644 000765 000024 00000000037 13040701535 017562 0ustar00dimitrisstaff000000 000000 0,1,1,2 1,0,2,1 0,0,0,1 1,0,0,0socnetv-2.2/nets/4actors-line-properties.dot000644 000765 000024 00000000511 13040701535 022177 0ustar00dimitrisstaff000000 000000 graph graphname { // The label attribute can be used to change the label of a node a [label="Foo"]; // Here, the node shape is changed. b [shape=box]; // These edges both have different line properties a -- b -- c [color=blue]; b -- d [style=dotted]; c -- d [color=#ddd, style=dotted]; }socnetv-2.2/nets/5-actors-plain-list.lst000644 000765 000024 00000000027 13040701535 021226 0ustar00dimitrisstaff000000 000000 1 2 3 4 2 3 4 5 3 4 5 1socnetv-2.2/nets/6actors-plain.dot000644 000765 000024 00000000144 13040701535 020165 0ustar00dimitrisstaff000000 000000 digraph mydot { "test-1" -> "test-2" -> "test-3" -> "test-4" -> "test-5" -> "test-6" -> "test-1"; } socnetv-2.2/nets/Freemans_EIES-1_n48.lst000644 000765 000024 00000012260 13040701535 020715 0ustar00dimitrisstaff000000 000000 1 2 4 1 3 2 1 6 2 1 8 2 1 10 2 1 11 2 1 13 2 1 14 2 1 18 2 1 19 2 1 20 2 1 21 2 1 22 2 1 23 2 1 24 2 1 25 2 1 26 3 1 27 2 1 31 2 1 32 2 1 33 2 1 35 2 1 36 2 1 37 2 1 38 2 1 39 3 1 40 2 1 41 2 1 42 2 1 43 2 1 44 4 1 45 2 1 46 2 2 1 4 2 3 2 2 8 1 2 11 3 2 13 3 2 14 4 2 18 1 2 19 3 2 21 2 2 22 2 2 23 2 2 24 3 2 25 2 2 27 1 2 32 2 2 33 3 2 35 2 2 37 2 2 40 2 2 41 1 2 42 2 2 43 3 2 44 4 2 45 4 2 46 2 3 1 3 3 2 1 3 6 4 3 8 1 3 13 2 3 18 2 3 19 4 3 20 4 3 22 4 3 23 1 3 24 2 3 25 2 3 26 2 3 27 1 3 31 1 3 32 2 3 33 2 3 35 2 3 36 4 3 37 2 3 39 2 3 41 1 3 42 1 3 43 1 6 1 2 6 3 2 6 8 2 6 13 2 6 14 2 6 18 2 6 19 2 6 20 2 6 21 2 6 22 2 6 23 2 6 24 1 6 27 4 6 31 1 6 32 2 6 33 2 6 35 2 6 36 2 6 37 2 6 38 2 6 40 2 6 41 2 6 42 2 6 44 2 8 1 3 8 6 2 8 13 2 8 14 3 8 18 2 8 19 2 8 20 1 8 22 2 8 23 1 8 24 2 8 25 2 8 27 1 8 32 2 8 33 2 8 35 2 8 37 2 8 38 1 8 40 1 8 41 2 8 42 2 8 44 2 8 45 2 10 1 3 10 13 2 10 22 2 10 24 1 10 27 2 10 33 1 10 40 2 10 42 2 10 44 2 11 1 3 11 2 2 11 3 1 11 13 2 11 14 2 11 19 1 11 21 3 11 41 2 13 1 2 13 2 2 13 3 2 13 6 2 13 8 2 13 14 1 13 19 2 13 21 2 13 22 2 13 23 2 13 24 2 13 25 2 13 27 1 13 32 2 13 33 2 13 35 1 13 36 1 13 37 2 13 38 2 13 40 2 13 42 2 13 43 2 14 1 3 14 2 4 14 8 2 14 13 2 14 19 1 14 21 2 14 22 1 14 33 1 14 35 3 14 40 3 14 45 4 18 1 2 18 2 1 18 3 3 18 6 3 18 8 2 18 11 1 18 13 2 18 14 2 18 19 2 18 20 3 18 22 1 18 23 2 18 24 2 18 25 2 18 27 2 18 31 2 18 32 3 18 33 2 18 35 2 18 36 4 18 37 2 18 38 2 18 41 2 18 42 2 18 43 2 19 1 1 19 2 3 19 3 2 19 6 1 19 8 1 19 13 3 19 14 1 19 18 1 19 22 2 19 23 1 19 24 2 19 25 2 19 27 1 19 31 2 19 32 2 19 33 2 19 35 2 19 36 1 19 37 2 19 38 2 19 40 2 19 41 1 19 42 1 19 44 1 20 1 1 20 3 1 20 6 2 20 13 1 20 18 3 20 22 2 20 24 1 20 27 2 20 32 2 20 33 2 20 35 2 20 38 2 20 42 2 20 43 2 21 1 3 21 2 3 21 3 1 21 6 2 21 8 1 21 11 3 21 13 3 21 14 2 21 18 1 21 19 1 21 22 1 21 23 1 21 24 1 21 27 2 21 31 1 21 32 1 21 33 1 21 35 1 21 36 1 21 39 2 21 40 4 21 41 2 21 42 2 21 43 2 21 44 3 21 45 3 22 1 3 22 2 2 22 3 4 22 6 2 22 8 3 22 13 3 22 14 2 22 18 1 22 19 2 22 20 3 22 21 1 22 23 3 22 24 4 22 25 3 22 26 2 22 27 3 22 31 2 22 32 3 22 33 3 22 35 4 22 36 3 22 37 3 22 38 3 22 39 2 22 40 1 22 41 2 22 42 4 22 43 3 22 44 2 22 46 1 23 1 3 23 2 2 23 3 2 23 6 3 23 8 1 23 11 1 23 13 2 23 14 2 23 18 2 23 19 2 23 20 1 23 22 3 23 24 2 23 25 2 23 27 2 23 31 4 23 32 1 23 33 2 23 35 1 23 36 2 23 37 2 23 38 2 23 42 3 23 44 2 23 46 1 24 1 2 24 2 2 24 3 2 24 6 1 24 8 3 24 13 3 24 14 1 24 19 2 24 22 3 24 23 2 24 25 3 24 27 1 24 31 2 24 32 2 24 33 4 24 35 3 24 37 3 24 38 2 24 42 2 25 1 3 25 2 2 25 3 3 25 8 2 25 13 3 25 14 2 25 18 1 25 19 2 25 22 3 25 23 2 25 24 2 25 27 1 25 32 3 25 33 3 25 35 3 25 37 2 25 41 1 25 42 1 25 44 2 25 46 1 26 1 4 26 2 1 26 3 2 26 19 2 26 22 2 26 23 1 26 27 1 26 37 1 26 39 2 26 40 2 26 41 1 26 42 2 26 43 2 26 44 4 26 46 2 27 1 2 27 3 2 27 6 4 27 8 1 27 13 2 27 18 2 27 20 2 27 22 2 27 23 2 27 24 1 27 32 1 27 33 2 27 35 3 27 36 2 27 37 2 27 38 2 27 39 2 27 41 2 27 42 2 27 43 1 27 44 2 27 46 2 31 1 1 31 3 2 31 6 1 31 8 1 31 18 2 31 19 2 31 20 2 31 22 2 31 23 2 31 24 2 31 32 1 31 35 3 31 36 1 31 37 3 31 38 2 31 42 1 32 1 2 32 2 2 32 3 2 32 6 2 32 8 2 32 13 2 32 18 3 32 19 2 32 20 2 32 22 3 32 23 1 32 24 2 32 25 2 32 27 2 32 31 1 32 33 3 32 35 4 32 36 2 32 37 3 32 38 3 32 41 2 32 42 3 32 43 1 33 1 3 33 2 3 33 3 2 33 6 2 33 8 2 33 13 3 33 14 1 33 18 2 33 19 3 33 20 2 33 22 2 33 23 3 33 24 4 33 25 3 33 27 2 33 31 2 33 32 2 33 35 3 33 36 2 33 37 2 33 38 3 33 40 1 33 41 2 33 42 2 33 43 1 33 45 1 35 1 2 35 2 2 35 3 2 35 6 3 35 13 2 35 14 3 35 18 2 35 19 2 35 22 3 35 24 3 35 25 2 35 27 3 35 32 3 35 33 3 35 37 4 35 38 2 35 41 2 35 42 4 36 1 2 36 3 4 36 6 3 36 18 4 36 20 1 36 22 2 36 23 1 36 24 1 36 27 2 36 31 1 36 32 2 36 33 2 36 35 1 36 37 1 36 38 2 36 41 1 36 42 2 37 1 2 37 2 2 37 3 2 37 6 2 37 8 2 37 13 3 37 14 2 37 18 2 37 19 2 37 20 2 37 22 3 37 23 2 37 24 3 37 25 2 37 27 3 37 31 4 37 32 3 37 33 3 37 35 4 37 36 2 37 38 3 37 40 2 37 41 2 37 42 4 38 1 2 38 2 2 38 3 2 38 6 2 38 8 1 38 13 2 38 18 3 38 19 2 38 20 2 38 22 3 38 23 2 38 24 3 38 27 2 38 31 2 38 32 4 38 33 3 38 35 3 38 36 3 38 37 4 38 41 1 39 1 4 39 2 1 39 3 2 39 6 1 39 8 1 39 11 1 39 13 1 39 18 1 39 19 1 39 20 1 39 21 2 39 22 2 39 23 1 39 24 1 39 26 3 39 27 2 39 32 1 39 33 1 39 35 2 39 36 1 39 37 2 39 38 1 39 41 2 39 42 2 39 44 3 39 46 1 40 1 2 40 2 2 40 3 1 40 6 2 40 8 1 40 13 2 40 14 2 40 18 1 40 19 1 40 21 4 40 22 1 40 23 1 40 24 1 40 25 1 40 27 1 40 32 1 40 33 1 40 43 2 41 1 3 41 2 2 41 6 3 41 18 1 41 19 1 41 21 1 41 22 2 41 23 2 41 24 2 41 27 3 41 32 2 41 33 2 41 35 3 41 37 2 41 38 1 41 39 2 41 40 1 41 42 2 41 44 2 42 1 2 42 2 2 42 3 2 42 6 2 42 8 2 42 13 2 42 18 2 42 20 2 42 22 3 42 23 2 42 24 2 42 26 2 42 27 2 42 32 2 42 33 2 42 35 4 42 36 2 42 37 3 42 39 2 42 40 2 42 41 2 42 43 2 42 44 2 42 46 3 43 1 3 43 2 4 43 3 1 43 13 4 43 18 2 43 21 2 43 22 2 43 24 2 43 25 2 43 26 2 43 27 2 43 32 2 43 33 2 43 35 2 43 40 2 43 41 1 43 42 2 43 45 2 43 46 1 44 1 4 44 2 4 44 3 2 44 6 2 44 8 2 44 10 2 44 11 1 44 13 2 44 14 2 44 19 2 44 21 2 44 22 2 44 23 2 44 24 1 44 25 2 44 26 3 44 27 2 44 33 1 44 35 2 44 36 2 44 37 2 44 39 2 44 40 2 44 41 2 44 42 2 44 43 2 44 46 1 45 1 3 45 2 3 45 6 1 45 8 2 45 13 3 45 14 4 45 19 1 45 21 2 45 22 1 45 24 1 45 27 1 45 32 1 45 33 1 45 40 2 45 43 3 45 44 3 45 46 1 46 1 2 46 2 2 46 42 3socnetv-2.2/nets/Freemans_EIES-2_n48.lst000644 000765 000024 00000014325 13040701535 020722 0ustar00dimitrisstaff000000 000000 1 2 4 1 3 2 1 6 2 1 8 2 1 10 2 1 11 2 1 13 3 1 14 3 1 18 2 1 19 3 1 20 2 1 21 3 1 22 2 1 23 2 1 24 2 1 25 2 1 26 3 1 27 2 1 31 2 1 32 2 1 33 2 1 35 2 1 36 2 1 37 2 1 38 2 1 39 3 1 40 2 1 41 2 1 42 3 1 43 2 1 44 4 1 45 3 1 46 3 2 1 4 2 3 2 2 6 2 2 8 1 2 10 2 2 11 2 2 13 3 2 14 4 2 18 2 2 19 3 2 21 2 2 22 2 2 23 2 2 24 2 2 25 2 2 26 2 2 27 2 2 32 2 2 33 2 2 35 2 2 36 2 2 37 2 2 38 2 2 39 2 2 40 2 2 41 2 2 42 2 2 43 3 2 44 4 2 45 4 2 46 2 3 1 3 3 2 1 3 6 4 3 8 1 3 13 2 3 18 2 3 19 4 3 20 4 3 22 4 3 23 1 3 24 2 3 25 2 3 26 2 3 27 1 3 31 1 3 32 2 3 33 2 3 35 2 3 36 4 3 37 2 3 39 2 3 41 1 3 42 1 3 43 1 6 1 2 6 2 2 6 3 2 6 8 2 6 10 2 6 13 2 6 14 2 6 18 3 6 19 2 6 20 2 6 21 1 6 22 2 6 23 2 6 24 2 6 26 2 6 27 4 6 31 1 6 32 2 6 33 2 6 35 2 6 36 2 6 37 2 6 38 2 6 39 2 6 40 2 6 41 2 6 42 2 6 43 2 6 44 2 6 46 2 8 1 3 8 6 2 8 13 2 8 14 3 8 18 2 8 19 2 8 20 1 8 22 2 8 23 1 8 24 2 8 25 2 8 27 1 8 32 2 8 33 2 8 35 2 8 37 2 8 38 1 8 40 1 8 41 2 8 42 2 8 44 2 8 45 2 10 1 4 10 2 2 10 13 3 10 18 2 10 19 2 10 22 2 10 23 2 10 24 2 10 27 2 10 31 2 10 33 2 10 37 3 10 39 2 10 40 2 10 41 2 10 42 3 10 44 4 10 45 2 10 46 3 11 1 3 11 2 2 11 3 1 11 13 2 11 14 2 11 19 1 11 21 3 11 41 2 13 1 3 13 2 2 13 3 2 13 6 2 13 8 2 13 10 2 13 11 1 13 14 1 13 18 2 13 19 4 13 20 1 13 21 2 13 22 2 13 23 2 13 24 2 13 25 2 13 26 2 13 27 2 13 32 2 13 33 2 13 35 2 13 36 2 13 37 2 13 38 2 13 40 2 13 41 1 13 42 2 13 43 2 13 44 2 13 45 4 13 46 2 14 1 3 14 2 4 14 8 2 14 13 2 14 19 2 14 21 2 14 22 1 14 24 1 14 25 2 14 33 2 14 35 2 14 40 3 14 42 1 14 44 2 14 45 4 14 46 2 18 1 3 18 3 2 18 6 3 18 8 2 18 11 1 18 13 2 18 14 1 18 19 2 18 20 3 18 21 2 18 22 1 18 23 2 18 24 2 18 25 2 18 26 2 18 27 2 18 31 2 18 32 4 18 33 2 18 35 2 18 36 4 18 37 2 18 38 2 18 40 2 18 41 2 18 42 3 18 43 2 18 44 2 18 45 1 19 1 3 19 2 2 19 3 2 19 6 2 19 8 2 19 10 2 19 13 4 19 14 2 19 18 2 19 21 2 19 22 2 19 23 2 19 24 2 19 25 2 19 26 2 19 27 2 19 31 2 19 32 2 19 33 2 19 35 2 19 36 1 19 37 2 19 38 2 19 40 2 19 41 2 19 42 2 19 44 3 19 45 3 19 46 2 20 1 2 20 3 1 20 6 2 20 11 1 20 13 1 20 18 3 20 22 2 20 23 1 20 24 1 20 27 2 20 31 2 20 32 3 20 33 2 20 35 1 20 36 1 20 37 1 20 38 2 20 41 1 20 43 1 20 44 2 20 45 2 20 46 2 21 1 3 21 2 3 21 3 1 21 6 2 21 8 1 21 11 3 21 13 3 21 14 2 21 18 1 21 19 2 21 22 1 21 23 1 21 24 2 21 26 2 21 27 2 21 31 1 21 32 1 21 33 2 21 35 2 21 36 1 21 39 2 21 40 4 21 41 2 21 42 2 21 43 2 21 44 3 21 45 3 22 1 3 22 2 2 22 3 4 22 6 3 22 8 3 22 13 3 22 18 2 22 19 2 22 20 3 22 21 2 22 23 3 22 24 4 22 25 4 22 26 2 22 27 3 22 31 3 22 32 3 22 33 3 22 35 4 22 36 3 22 37 3 22 38 3 22 39 2 22 40 2 22 41 3 22 42 4 22 43 3 22 44 3 22 45 2 22 46 2 23 1 3 23 2 2 23 3 2 23 6 3 23 8 1 23 13 2 23 14 2 23 18 2 23 19 2 23 20 2 23 22 3 23 24 2 23 25 2 23 27 2 23 31 4 23 32 1 23 33 2 23 35 2 23 36 2 23 37 2 23 38 2 23 40 1 23 42 3 23 44 3 23 45 1 24 1 2 24 2 2 24 3 2 24 6 2 24 8 3 24 10 2 24 13 3 24 14 1 24 18 2 24 19 2 24 22 3 24 23 2 24 25 2 24 27 2 24 31 2 24 32 2 24 33 4 24 35 3 24 37 2 24 38 2 24 39 2 24 40 1 24 41 1 24 42 2 24 44 2 24 45 2 24 46 2 25 1 3 25 2 2 25 3 3 25 6 1 25 8 2 25 13 3 25 14 2 25 18 1 25 19 3 25 20 1 25 21 1 25 22 3 25 23 2 25 24 3 25 26 1 25 27 1 25 32 3 25 33 3 25 35 3 25 37 2 25 39 1 25 40 2 25 41 1 25 42 2 25 43 2 25 44 2 25 45 2 25 46 1 26 1 4 26 2 2 26 3 2 26 11 1 26 13 2 26 19 2 26 21 1 26 22 2 26 39 2 26 40 2 26 42 2 26 43 2 26 44 4 26 45 1 26 46 2 27 1 2 27 3 2 27 6 4 27 8 1 27 13 2 27 18 2 27 20 2 27 22 2 27 23 2 27 24 1 27 32 2 27 33 2 27 35 3 27 36 2 27 37 2 27 38 2 27 39 2 27 41 2 27 42 2 27 43 1 27 44 2 27 46 2 31 1 1 31 3 2 31 6 1 31 8 1 31 18 2 31 19 2 31 20 2 31 22 2 31 23 2 31 24 2 31 32 1 31 35 3 31 36 1 31 37 3 31 38 2 31 42 1 32 1 2 32 2 2 32 3 2 32 6 2 32 8 2 32 13 2 32 18 3 32 19 2 32 20 2 32 22 3 32 23 1 32 24 2 32 25 2 32 27 2 32 31 1 32 33 3 32 35 4 32 36 2 32 37 3 32 38 3 32 41 2 32 42 3 32 43 1 33 1 3 33 2 3 33 3 2 33 6 2 33 8 2 33 13 3 33 14 1 33 18 2 33 19 3 33 20 2 33 22 2 33 23 3 33 24 4 33 25 3 33 27 2 33 31 2 33 32 2 33 35 3 33 36 2 33 37 2 33 38 3 33 40 1 33 41 2 33 42 2 33 43 1 33 45 1 35 1 2 35 2 2 35 3 2 35 6 3 35 13 2 35 14 3 35 18 2 35 19 2 35 22 3 35 23 2 35 24 3 35 25 2 35 27 3 35 32 3 35 33 3 35 37 4 35 38 2 35 41 2 35 42 4 35 45 2 36 1 3 36 2 2 36 3 4 36 6 3 36 13 2 36 18 4 36 20 1 36 22 3 36 23 1 36 24 1 36 27 3 36 31 1 36 32 2 36 33 1 36 35 1 36 37 2 36 38 2 36 41 2 36 42 3 36 43 2 36 44 2 36 46 2 37 1 3 37 2 2 37 3 2 37 6 2 37 8 3 37 10 2 37 13 3 37 14 2 37 18 2 37 19 3 37 20 2 37 21 2 37 22 3 37 23 2 37 24 3 37 25 2 37 26 2 37 27 2 37 31 4 37 32 3 37 33 3 37 35 4 37 36 2 37 38 3 37 40 2 37 41 3 37 42 3 37 43 2 37 44 2 37 45 2 37 46 2 38 1 2 38 2 2 38 3 2 38 6 3 38 8 1 38 13 3 38 18 3 38 19 2 38 20 2 38 22 3 38 23 2 38 24 3 38 27 2 38 31 3 38 32 3 38 33 3 38 35 3 38 36 3 38 37 3 38 41 1 38 42 2 39 1 4 39 2 1 39 3 2 39 6 1 39 8 1 39 11 1 39 13 1 39 18 1 39 19 1 39 20 1 39 21 2 39 22 2 39 23 1 39 24 1 39 26 3 39 27 2 39 32 1 39 33 1 39 35 2 39 36 1 39 37 2 39 38 1 39 41 2 39 42 2 39 44 3 39 46 1 40 1 3 40 2 2 40 3 2 40 6 2 40 8 2 40 10 2 40 13 3 40 14 3 40 18 2 40 19 2 40 21 4 40 22 1 40 23 2 40 24 2 40 25 2 40 26 2 40 27 2 40 32 1 40 33 2 40 35 2 40 36 1 40 37 2 40 42 2 40 43 2 40 44 2 40 45 2 40 46 2 41 1 3 41 2 2 41 6 3 41 13 2 41 18 1 41 19 1 41 21 2 41 22 2 41 23 2 41 24 2 41 27 3 41 32 2 41 33 2 41 35 3 41 37 2 41 38 1 41 39 2 41 40 1 41 42 2 41 44 2 41 45 2 41 46 2 42 1 3 42 2 2 42 3 2 42 6 3 42 8 2 42 10 2 42 13 3 42 18 3 42 19 2 42 20 3 42 21 2 42 22 4 42 23 3 42 24 2 42 25 2 42 26 2 42 27 2 42 32 3 42 33 2 42 35 4 42 36 2 42 37 4 42 39 2 42 40 2 42 41 2 42 43 2 42 44 3 42 45 2 42 46 4 43 1 3 43 2 3 43 3 1 43 6 2 43 10 2 43 13 3 43 18 2 43 19 2 43 21 2 43 22 2 43 24 2 43 25 2 43 26 2 43 27 2 43 32 2 43 33 2 43 35 2 43 37 2 43 38 2 43 40 3 43 41 2 43 42 3 43 44 3 43 45 3 43 46 2 44 1 4 44 2 4 44 3 2 44 6 2 44 8 2 44 10 3 44 11 2 44 13 2 44 14 2 44 18 2 44 19 3 44 20 2 44 21 3 44 22 2 44 23 3 44 24 2 44 25 2 44 26 3 44 27 2 44 31 2 44 32 2 44 33 2 44 35 2 44 36 2 44 37 2 44 38 2 44 39 2 44 40 2 44 41 2 44 42 3 44 43 2 44 45 4 44 46 3 45 1 4 45 2 4 45 6 2 45 8 2 45 10 2 45 13 4 45 14 4 45 18 2 45 19 3 45 21 2 45 22 1 45 24 3 45 25 2 45 32 1 45 33 2 45 35 3 45 36 1 45 37 1 45 39 2 45 40 2 45 41 1 45 42 3 45 43 2 45 44 4 45 46 3 46 1 3 46 2 2 46 6 1 46 8 1 46 10 2 46 13 3 46 14 2 46 18 2 46 19 2 46 24 2 46 25 2 46 27 1 46 37 2 46 42 4 46 44 3 46 45 3socnetv-2.2/nets/Freemans_EIES-3-messages_n48.lst000644 000765 000024 00000007142 13040701535 022527 0ustar00dimitrisstaff000000 000000 1 1 24 1 2 488 1 3 28 1 4 65 1 5 20 1 6 65 1 7 45 1 8 346 1 9 82 1 10 52 1 11 177 1 12 28 1 13 24 1 14 49 1 15 81 1 16 77 1 17 77 1 18 73 1 19 33 1 20 31 1 21 22 1 22 46 1 23 31 1 24 128 1 25 38 1 26 89 1 27 95 1 28 25 1 29 388 1 30 71 1 31 212 1 32 185 2 1 364 2 2 6 2 3 17 2 4 17 2 5 15 2 7 30 2 8 20 2 9 35 2 10 20 2 11 22 2 12 15 2 13 15 2 14 15 2 15 15 2 16 50 2 17 25 2 18 8 2 20 15 2 21 15 2 22 15 2 23 15 2 25 15 2 26 15 2 27 10 2 28 24 2 29 89 2 30 23 2 31 163 2 32 39 3 1 4 3 2 5 3 8 5 4 1 52 4 2 30 4 4 4 4 6 2 4 8 32 4 9 21 4 10 34 4 11 9 4 16 5 4 17 4 4 18 2 4 19 35 4 24 12 4 27 12 4 28 5 4 29 20 4 30 4 4 31 19 4 32 33 5 1 26 5 2 4 5 3 4 5 4 4 5 6 4 5 7 8 5 8 4 5 9 4 5 10 4 5 11 4 5 12 4 5 13 4 5 14 4 5 15 4 5 16 4 5 17 4 5 18 4 5 19 4 5 21 4 5 22 8 5 23 4 5 24 14 5 25 4 5 27 4 5 29 4 5 30 7 5 31 4 5 32 4 6 1 72 6 2 23 6 4 2 6 6 34 6 8 16 6 10 7 6 11 15 6 15 8 6 16 7 6 17 6 6 24 14 6 27 7 6 28 3 6 29 34 6 30 3 6 31 22 7 1 14 7 31 6 8 1 239 8 2 82 8 3 5 8 4 37 8 5 3 8 6 34 8 7 5 8 8 10 8 9 12 8 10 18 8 11 164 8 12 18 8 16 30 8 17 53 8 18 27 8 19 20 8 20 4 8 22 5 8 23 4 8 24 55 8 26 9 8 27 34 8 29 146 8 30 216 8 31 88 8 32 288 9 1 24 9 2 25 9 4 2 9 8 8 9 9 16 9 11 15 9 13 10 9 17 5 9 27 15 9 29 10 9 31 30 9 32 44 10 1 43 10 2 15 10 4 32 10 6 12 10 8 14 10 10 5 10 11 25 10 12 2 10 16 10 10 17 10 10 19 20 10 20 15 10 22 5 10 23 20 10 24 29 10 26 4 10 27 10 10 29 47 10 30 6 10 31 22 10 32 19 11 1 178 11 2 36 11 4 11 11 6 19 11 7 10 11 8 172 11 9 39 11 10 28 11 11 29 11 13 4 11 16 23 11 17 15 11 18 24 11 21 8 11 24 29 11 25 10 11 26 11 11 27 22 11 29 46 11 31 119 11 32 34 12 2 5 12 8 5 12 12 3 12 19 5 12 29 53 12 31 5 12 32 9 13 1 5 13 11 5 13 31 5 14 1 12 14 3 9 14 14 2 14 16 12 14 19 5 14 29 35 14 31 8 15 1 120 15 6 4 15 12 5 15 15 78 15 27 8 15 29 58 15 31 32 16 1 58 16 2 25 16 4 10 16 8 20 16 10 5 16 11 10 16 14 5 16 16 15 16 17 10 16 21 5 16 24 5 16 29 35 16 31 10 17 1 63 17 2 18 17 3 9 17 4 7 17 6 6 17 8 36 17 10 5 17 11 9 17 12 5 17 14 5 17 16 5 17 20 5 17 21 2 17 27 15 17 29 10 17 30 9 17 31 15 17 32 9 18 1 58 18 2 8 18 3 5 18 4 4 18 8 4 18 10 5 18 11 18 18 18 4 18 27 20 18 29 8 18 30 10 18 31 48 19 1 5 19 2 5 19 4 25 19 8 10 19 14 5 19 19 5 19 23 5 19 31 10 20 21 4 20 29 4 21 1 9 21 11 3 21 16 5 21 29 5 22 1 10 22 24 40 22 29 15 22 32 5 23 1 5 23 2 5 23 3 5 23 10 19 23 19 5 23 29 14 23 31 5 24 1 89 24 2 17 24 3 4 24 4 14 24 5 14 24 6 18 24 7 8 24 8 41 24 9 4 24 10 19 24 11 31 24 12 4 24 13 4 24 14 9 24 15 4 24 16 14 24 17 4 24 18 9 24 19 4 24 20 4 24 21 4 24 22 58 24 23 4 24 24 5 24 25 18 24 26 14 24 27 9 24 28 4 24 29 156 24 30 4 24 31 56 24 32 10 25 1 32 25 2 5 25 14 15 25 22 10 25 24 23 25 25 10 25 30 9 25 31 15 26 1 35 26 2 5 26 10 5 26 29 10 26 31 13 27 1 50 27 2 28 27 4 13 27 8 19 27 9 29 27 10 5 27 11 8 27 13 33 27 15 4 27 17 10 27 18 15 27 24 10 27 28 3 27 29 32 27 31 13 27 32 33 28 1 9 28 2 6 28 6 3 28 28 3 28 32 6 29 1 559 29 2 132 29 3 5 29 4 24 29 5 21 29 6 29 29 8 155 29 9 15 29 10 98 29 11 69 29 12 89 29 13 37 29 14 76 29 15 80 29 16 63 29 17 15 29 18 4 29 19 9 29 20 18 29 21 43 29 22 108 29 23 29 29 24 218 29 26 15 29 27 66 29 29 6 29 30 14 29 31 91 29 32 126 30 1 39 30 2 21 30 4 6 30 5 3 30 6 3 30 8 140 30 10 7 30 12 2 30 17 9 30 18 5 30 27 2 30 29 18 30 30 2 30 31 20 30 32 8 31 1 82 31 2 125 31 3 10 31 4 22 31 5 10 31 6 15 31 7 18 31 8 70 31 9 35 31 10 23 31 11 114 31 12 20 31 13 16 31 14 15 31 15 24 31 16 30 31 17 28 31 18 49 31 19 30 31 20 5 31 21 5 31 22 15 31 23 8 31 24 53 31 25 25 31 26 8 31 27 21 31 28 8 31 29 65 31 30 28 31 32 67 32 1 239 32 2 99 32 4 27 32 5 3 32 8 268 32 9 101 32 10 18 32 11 35 32 12 4 32 17 7 32 22 14 32 24 5 32 27 50 32 28 6 32 29 71 32 30 7 32 31 107 32 32 219socnetv-2.2/nets/jin1-sm.txt000644 000765 000024 00000001573 13040701535 017021 0ustar00dimitrisstaff000000 000000 0 1 1 1 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 socnetv-2.2/nets/jin2-sm.txt000644 000765 000024 00000000401 13040701535 017007 0ustar00dimitrisstaff000000 000000 0 1 1 1 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 socnetv-2.2/nets/jin3-sm.txt000644 000765 000024 00000000375 13040701535 017022 0ustar00dimitrisstaff000000 000000 0 1 1 1 1 1 0 0 1 1 1 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 socnetv-2.2/nets/jin4-sm.txt000644 000765 000024 00000000401 13040701535 017011 0ustar00dimitrisstaff000000 000000 0 1 1 1 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 socnetv-2.2/nets/lion_share.dot000644 000765 000024 00000013327 13040701535 017635 0ustar00dimitrisstaff000000 000000 ##"A few people in the field of genetics are using dot to draw "marriage node diagram" pedigree drawings. Here is one I have done of a test pedigree from the FTREE pedigree drawing package (Lion Share was a racehorse)." Contributed by David Duffy. ##Command to get the layout: "dot -Tpng thisfile > thisfile.png" digraph Ped_Lion_Share { # page = "8.2677165,11.692913" ; ratio = "auto" ; mincross = 2.0 ; label = "Pedigree Lion_Share" ; "001" [shape=box , regular=1,style=filled,fillcolor=white ] ; "002" [shape=box , regular=1,style=filled,fillcolor=white ] ; "003" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "004" [shape=box , regular=1,style=filled,fillcolor=white ] ; "005" [shape=box , regular=1,style=filled,fillcolor=white ] ; "006" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "007" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "009" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "014" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "015" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "016" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "ZZ01" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "ZZ02" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "017" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "012" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "008" [shape=box , regular=1,style=filled,fillcolor=white ] ; "011" [shape=box , regular=1,style=filled,fillcolor=white ] ; "013" [shape=box , regular=1,style=filled,fillcolor=white ] ; "010" [shape=box , regular=1,style=filled,fillcolor=white ] ; "023" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "020" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "021" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "018" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "025" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "019" [shape=box , regular=1,style=filled,fillcolor=white ] ; "022" [shape=box , regular=1,style=filled,fillcolor=white ] ; "024" [shape=box , regular=1,style=filled,fillcolor=white ] ; "027" [shape=circle , regular=1,style=filled,fillcolor=white ] ; "026" [shape=box , regular=1,style=filled,fillcolor=white ] ; "028" [shape=box , regular=1,style=filled,fillcolor=grey ] ; "marr0001" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "001" -> "marr0001" [dir=none,weight=1] ; "007" -> "marr0001" [dir=none,weight=1] ; "marr0001" -> "017" [dir=none, weight=2] ; "marr0002" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "001" -> "marr0002" [dir=none,weight=1] ; "ZZ02" -> "marr0002" [dir=none,weight=1] ; "marr0002" -> "012" [dir=none, weight=2] ; "marr0003" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "002" -> "marr0003" [dir=none,weight=1] ; "003" -> "marr0003" [dir=none,weight=1] ; "marr0003" -> "008" [dir=none, weight=2] ; "marr0004" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "002" -> "marr0004" [dir=none,weight=1] ; "006" -> "marr0004" [dir=none,weight=1] ; "marr0004" -> "011" [dir=none, weight=2] ; "marr0005" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "002" -> "marr0005" [dir=none,weight=1] ; "ZZ01" -> "marr0005" [dir=none,weight=1] ; "marr0005" -> "013" [dir=none, weight=2] ; "marr0006" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "004" -> "marr0006" [dir=none,weight=1] ; "009" -> "marr0006" [dir=none,weight=1] ; "marr0006" -> "010" [dir=none, weight=2] ; "marr0007" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "005" -> "marr0007" [dir=none,weight=1] ; "015" -> "marr0007" [dir=none,weight=1] ; "marr0007" -> "023" [dir=none, weight=2] ; "marr0008" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "005" -> "marr0008" [dir=none,weight=1] ; "016" -> "marr0008" [dir=none,weight=1] ; "marr0008" -> "020" [dir=none, weight=2] ; "marr0009" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "005" -> "marr0009" [dir=none,weight=1] ; "012" -> "marr0009" [dir=none,weight=1] ; "marr0009" -> "021" [dir=none, weight=2] ; "marr0010" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "008" -> "marr0010" [dir=none,weight=1] ; "017" -> "marr0010" [dir=none,weight=1] ; "marr0010" -> "018" [dir=none, weight=2] ; "marr0011" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "011" -> "marr0011" [dir=none,weight=1] ; "023" -> "marr0011" [dir=none,weight=1] ; "marr0011" -> "025" [dir=none, weight=2] ; "marr0012" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "013" -> "marr0012" [dir=none,weight=1] ; "014" -> "marr0012" [dir=none,weight=1] ; "marr0012" -> "019" [dir=none, weight=2] ; "marr0013" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "010" -> "marr0013" [dir=none,weight=1] ; "021" -> "marr0013" [dir=none,weight=1] ; "marr0013" -> "022" [dir=none, weight=2] ; "marr0014" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "019" -> "marr0014" [dir=none,weight=1] ; "020" -> "marr0014" [dir=none,weight=1] ; "marr0014" -> "024" [dir=none, weight=2] ; "marr0015" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "022" -> "marr0015" [dir=none,weight=1] ; "025" -> "marr0015" [dir=none,weight=1] ; "marr0015" -> "027" [dir=none, weight=2] ; "marr0016" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "024" -> "marr0016" [dir=none,weight=1] ; "018" -> "marr0016" [dir=none,weight=1] ; "marr0016" -> "026" [dir=none, weight=2] ; "marr0017" [shape=diamond,style=filled,label="",height=.1,width=.1] ; "026" -> "marr0017" [dir=none,weight=1] ; "027" -> "marr0017" [dir=none,weight=1] ; "marr0017" -> "028" [dir=none, weight=2] ; } socnetv-2.2/nets/mexico-power-network.lst000644 000765 000024 00000000500 13040701535 021620 0ustar00dimitrisstaff000000 000000 18 8 10 23 21 19 11 21 29 5 9 10 23 8 9 18 11 4 7 6 8 20 5 21 5 4 29 20 7 6 8 9 26 21 6 5 7 4 20 21 8 7 4 6 5 8 20 21 9 5 8 23 29 20 21 11 10 8 18 23 4 5 6 7 21 24 26 25 9 10 37 20 10 18 29 8 11 9 20 25 26 11 19 23 9 10 25 21 36 20 4 5 6 7 8 9 10 24 8 26 26 5 8 24 10 21 19 4 5 6 7 8 9 11 18 36 37 11 37 8 36 25 10 11 8 socnetv-2.2/nets/network-edgelist.dl000644 000765 000024 00000000614 13040701535 020605 0ustar00dimitrisstaff000000 000000 DL N=4 FORMAT = edgelist LABELS: On the normalization and visualization of author co-citation data:Salton's cosine versus the Jaccard index Caveats for the use of citation indicators in research and journalevaluations Should co-occurrence data be normalized? A rejoinder Home on the range - What and where is the middle in science andtechnology studies? DATA: 1 2 1 1 3 5 1 4 2 2 3 1 2 4 1 3 4 1socnetv-2.2/nets/network.dl000644 000765 000024 00000000652 13040701535 017011 0ustar00dimitrisstaff000000 000000 DL N=4 FORMAT = FULLMATRIX DIAGONAL PRESENT LABELS: On the normalization and visualization of author co-citation data:Salton's cosine versus the Jaccard index Caveats for the use of citation indicators in research and journalevaluations Should co-occurrence data be normalized? A rejoinder Home on the range - What and where is the middle in science andtechnology studies? DATA: 0 0 0.158114 0 0.201234 0 1 0 1 0 0 0 0.1 1 1 0socnetv-2.2/nets/network.gw000644 000765 000024 00000002537 13040701535 017033 0ustar00dimitrisstaff000000 000000 LEDA.GRAPH unknown unknown 5 |{1 0 1}| |{2 0 1}| |{3 0 1}| |{4 0 1}| |{5 0 1}| 4 1 2 0 |{edge 3 1 3 1}| 2 3 0 |{edge 3 1 3 1}| 3 4 0 |{edge 3 1 3 1}| 4 5 0 |{edge 3 1 3 1}| # version string GraphWin 1.400000 # scaling wxmin wymin wxmax wymax 0.9821777 -274.8779 -281.2244 274.8779 283.8025 # node label font and size 0 15 # edge label font and size 0 13 # node index format %d # edge index format %d # multi-edge distance 4.793233 # # node infos # x y shape bclr(r,g,b) bwidth r1 r2 clr(r,g,b) ltype lclr(r,g,b) lpos lstr -146.6016 65.93628 0 0 0 0 1.018519 10.18066 10.18066 160 0 0 1 255 255 228 4 1 59.04785 107.677 0 0 0 0 1.018519 10.18066 10.18066 160 0 0 1 255 255 228 4 2 54.97559 -44.01489 0 0 0 0 1.018519 10.18066 10.18066 160 0 0 1 255 255 228 4 3 -145.5835 -44.01489 6 0 0 0 1.018519 20.36133 10.18066 160 0 0 1 255 255 228 4 4 -73.30078 -148.8757 0 0 0 0 1.018519 10.18066 10.18066 160 0 0 1 255 255 228 4 5 # # edge infos # width clr(r,g,b) shape style dir ltype lclr(r,g,b) lpos sanch tanch poly lstr 3.054437 0 0 0 0 0 1 0 0 0 0 3 (0,0) (0,0) 2 (-136.6243,67.96136) (49.07063,105.6519) 1 3.054437 0 0 0 0 0 1 0 0 0 0 3 (0,0) (0,0) 2 (58.77464,97.5) (55.24879,-33.8379) 1 3.054437 0 0 0 0 0 1 0 0 0 0 3 (0,0) (0,0) 2 (44.79492,-44.01489) (-125.2222,-44.01489) 1 3.054437 0 0 0 0 0 1 0 0 0 0 3 (0,0) (0,0) 2 (-140.3645,-51.58607) (-79.07878,-140.4936) 1 socnetv-2.2/nets/sampson.dl000644 000765 000024 00000016313 13040701535 017001 0ustar00dimitrisstaff000000 000000 DL N=18 NM=10 FORMAT = FULLMATRIX DIAGONAL PRESENT ROW LABELS: ROMUL_10 BONAVEN_5 AMBROSE_9 BERTH_6 PETER_4 LOUIS_11 VICTOR_8 WINF_12 JOHN_1 GREG_2 HUGH_14 BONI_15 MARK_7 ALBERT_16 AMAND_13 BASIL_3 ELIAS_17 SIMP_18 COLUMN LABELS: ROMUL_10 BONAVEN_5 AMBROSE_9 BERTH_6 PETER_4 LOUIS_11 VICTOR_8 WINF_12 JOHN_1 GREG_2 HUGH_14 BONI_15 MARK_7 ALBERT_16 AMAND_13 BASIL_3 ELIAS_17 SIMP_18 LEVEL LABELS: SAMPLK1 SAMPLK2 SAMPLK3 SAMPDLK SAMPES SAMPDES SAMPIN SAMPNIN SAMPPR SAMNPR DATA: 0 0 2 0 3 0 0 0 0 0 0 0 0 1 0 0 0 0 3 0 0 0 0 0 2 0 0 0 0 0 0 1 0 0 0 0 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 3 1 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 2 0 1 0 0 0 0 0 1 0 0 3 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 0 0 0 3 0 0 3 2 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 3 0 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 1 0 0 0 0 0 2 0 0 0 0 0 0 0 0 3 0 0 1 0 0 0 0 0 2 0 0 0 0 3 2 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 2 0 1 0 0 0 0 0 0 0 0 0 0 3 3 0 0 0 0 0 0 0 0 0 1 2 0 0 2 0 0 0 3 2 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 2 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 2 0 0 0 0 1 2 3 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 1 0 0 3 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 1 0 0 0 0 2 0 0 1 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 0 0 0 0 3 2 0 0 0 0 1 0 0 0 0 0 0 0 2 0 0 3 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 3 0 0 0 0 2 0 0 0 0 0 0 0 0 0 1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 3 0 1 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 3 0 0 0 2 0 0 0 0 0 0 0 0 0 3 1 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 2 3 0 0 0 0 0 0 0 0 0 0 1 0 0 3 1 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 2 0 1 0 0 0 0 0 0 0 0 0 0 3 3 1 0 0 0 0 0 0 0 0 0 1 0 0 2 0 0 0 2 3 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 3 0 0 0 0 1 0 0 0 0 0 0 0 1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 1 0 0 0 0 0 0 0 0 0 0 2 0 0 0 3 0 0 0 3 0 0 0 0 0 0 0 0 1 0 2 0 0 0 0 3 0 0 0 0 0 1 0 0 0 0 2 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 1 2 0 0 0 0 3 1 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 1 0 2 0 0 0 0 0 0 0 0 0 0 3 1 0 0 0 2 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 1 0 0 0 2 0 0 0 0 0 3 0 2 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 3 0 0 0 2 0 0 0 0 0 0 0 0 0 3 1 0 0 0 1 0 0 0 2 0 0 0 0 0 0 0 0 0 2 0 0 3 0 0 0 0 0 1 0 0 0 0 3 2 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 1 0 0 0 0 0 0 0 0 0 0 3 3 0 0 0 0 0 0 0 0 0 0 1 0 0 2 0 0 0 0 3 0 0 0 0 1 0 0 0 0 2 0 0 0 0 0 0 0 3 0 0 0 0 2 0 0 0 0 0 0 0 1 0 0 0 0 1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 1 0 0 0 0 0 0 0 0 0 1 0 0 2 0 0 3 2 0 0 0 0 0 0 0 0 0 2 3 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 3 1 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 2 0 0 3 0 0 0 0 0 0 0 0 0 3 0 2 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 3 0 2 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 3 2 0 0 0 0 0 2 3 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 1 2 0 0 0 0 2 0 0 0 1 0 3 0 0 0 0 0 0 0 0 0 0 0 2 3 0 0 0 0 2 0 0 0 1 0 0 0 0 0 0 0 3 1 0 2 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 1 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 3 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 1 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 1 0 0 0 0 0 0 0 0 1 0 0 0 0 3 2 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 2 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 2 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 3 0 1 0 2 0 0 0 0 0 0 0 0 0 0 0 1 0 3 0 2 2 0 0 0 0 0 0 3 0 0 0 1 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 2 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 2 0 3 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 3 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 3 2 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 2 2 0 0 0 0 0 0 0 0 0 2 3 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 0 0 0 1 0 2 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 3 0 0 1 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 1 2 0 0 0 0 0 0 2 2 0 0 0 0 0 0 0 0 3 0 1 1 0 0 0 0 3 1 0 0 0 0 0 0 0 0 0 2 1 1 0 0 0 3 2 0 2 0 0 0 0 0 0 0 0 2 1 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 2 2 1 0 0 0 3 0 0 0 0 0 2 0 0 0 0 0 1 0 0 0 0 0 2 3 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 3 2 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 2 2 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 3 2 0 0 0 0 0 0 0 0 0 0 2 0 3 0 0 0 0 1 0 0 0 0 0 0 0 0 2 0 0 1 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 3 2 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 2 0 3 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 2 3 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 3 2 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 3 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 1 2 0 0 0 0 0 0 3 0 0 0 1 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 2 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 2 0 3 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 2 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 3 2 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 2 1 0 0 0 0 0 0 0 0 2 3 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 1 2 0 0 0 0 0 0 0 1 0 0 0 0 0 3 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 3 0 0 0 0 3 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 3 0 1 1 0 0 0 0 3 0 0 0 0 0 0 0 0 0 2 1 0 0 0 0 0 3 2 0 2 0 0 0 0 0 0 0 0 2 1 0 0 0 0 0 3 2 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 1 0 3 0 0 0 0 0 0 0 0 0 0 0 2 3 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 3 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 3 2 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 1 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 2 0 0 0 0 0 0 1 0 0 0 0 0 0 0 2 3 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 3 0 0 0 0 0 1 0 0 0 0 0 0 0 0 2 3 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 3 3 0 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 3 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 1 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 1 3 0 0 0 0 0 0 3 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 1 0 2 1 0 0 0 0 0 0 0 0 0 3 2 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 3 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 1 3 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 3 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 3 2 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 3 2 0 1 0 0 0 1 2 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 1 2 2 2 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 2 3 0 0 0 0 3 0 0 0 0 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 socnetv-2.2/man/socnetv.1.gz000644 000765 000024 00000004327 13040734202 016763 0ustar00dimitrisstaff000000 000000 Xsocnetv.1Xko7_qaJ$-E`TqX^v@P3Ԉ5g8 9﹗3ths_(,h(KX8D&ʚ [\`'hӤwHB$$v1] O4HlJ+cu)^ c@hZkۈ狳n:O`H&ܻƍUq|5g+ot]-&W>ŵN^ ;@׊Cۧ!ѢJA,"KW: icrsG(\ kS^,xV޸6 ,|r=IuoGtO#?ޜߍH|(U0-{\QtZ]`pX^մQRVQ]}4: '_ ]0a Qչa|!Rx`]F2m-[9gQPµ@B"$@a튔U?ad:.Z\[~KL:m.^L4 a~CX$eKΪWhXXWK]ڏȫs Vޔkm0LdgU־KI.v}Z^ ! W/\u5#b ETY$`c^+62pVmYƱ%e@njfj*U]yOHRg]' |TRB?k ;g?Rw']O"k}21MsUn42iG3Dhwa7gwIDH'JYW܃") \otZ. w'u~1<ߡVT .H6uϭtZVh$1aFeiʒ؛r(_XiEH a\AW;Jm6H]%SEɿF+uNZ tO)?!b:8VEqu垵HE8{R H]}FknKA_hVލ‚ qڮ(&YAK5R4P_:X[ŠP BܦOp걔#ֳ">p}+KLQprX|A"j@*~o+JU[hkGt"83Q\2`*K 6Iս Q4NNokBlNU]"zqj-摰a ]u]N[tpJ"]0H4Ǵ,-uu~li[`|nSh%lɕN: RkSXQ?8+\heVv@@n%m(/3^ɒBN6caHN>87vio tJ8-E9~% [.OiZ1YbogN aq!1KxM:*--]uHkƲ⁍k^7ċG Z1RHa8c^W{"|7e#9ݔI f*,O7Zv}Foh֮5H|P7a:Zi'tbryg6-\$̾Чi g L44w~ 'zqa}G¢VR]`uK*$r@aͤfc@ω_m_/ X W nSZB<)@*s8> [RJbrw5TuweU},_8緳b0\>eX؄_ON;u: 'ݻy3\;B SC^%l| okxF:4^̤8dz?`? @H1s