修改为标准python包形式

This commit is contained in:
py2cn 2021-10-28 00:18:07 +08:00
parent de02877105
commit b63ae81568
8749 changed files with 973049 additions and 438894 deletions

5
.flake8 Normal file
View File

@ -0,0 +1,5 @@
[flake8]
ignore = W293,E301,E271,E265,W291,E722,E302,C901,E225,E128,E122,E226,E231
max-line-length = 160
exclude = tests/*
max-complexity = 10

7
.gitignore vendored
View File

@ -79,16 +79,13 @@ __pycache__/
.env
.venv
env/
venv/
venv*/
ENV/
env.bak/
venv.bak/
.vscode/
pyminer/config/qtlayout.ini
pyminer2/config/qtlayout.ini
pyminer2/config/customized/*
pyminer2/extensions/packages/code_editor/customized/settings.json
features/extensions/packages/code_editor/customized/settings.json
*.pptx
test.py

2
.mailmap Normal file
View File

@ -0,0 +1,2 @@
pyminer development team <team@py2cn.com>
lixianglong <aboutlong@qq.com>

8
.pypirc Normal file
View File

@ -0,0 +1,8 @@
[distutils]
index-servers =
pypi
[pypi]
repository = https://upload.pypi.org/legacy/
username = pyminer
password = pypi-AgEIcHlwaS5vcmcCJDBlYTM4NTgwLTVkZTktNDMyNy05MGU1LTEyZTgzZDkwNTZjYgACOHsicGVybWlzc2lvbnMiOiB7InByb2plY3RzIjogWyJweW1pbmVyIl19LCAidmVyc2lvbiI6IDF9AAAGIKggj-ecVRPT3zwKNytQivI-usyMhsPTHuPIrjZVO5aB

76
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at team@pyminer.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

827
LICENSE
View File

@ -1,674 +1,165 @@
GNU GENERAL PUBLIC LICENSE
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser 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
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

42
MANIFEST.in Normal file
View File

@ -0,0 +1,42 @@
include README.md
include LICENSE
include setup.py
include MANIFEST.in
include pytest.ini
include .mailmap
include .flake8
recursive-exclude tools *
exclude tools
exclude dist
exclude CONTRIBUTING.md
exclude .editorconfig
# Load main dir but exclude things we don't want in the distro
graft pyminer
# Documentation
graft docs
exclude docs/\#*
exclude docs/man/*.1.gz
exclude .git-blame-ignore-revs
# Examples
graft examples
# docs subdirs we want to skip
prune docs/build
prune docs/gh-pages
prune docs/dist
# Patterns to exclude from any directory
global-exclude *~
global-exclude *.flc
global-exclude *.yml
global-exclude *.pyc
global-exclude *.pyo
global-exclude .dircopy.log
global-exclude .git
global-exclude .ipynb_checkpoints

159
README.md
View File

@ -2,16 +2,16 @@
<p></p>
<p align="center">
<img src="pyminer2/ui/source/icons/logo.png" height="80"/>
<img src="pyminer/resources/icons/logo.png" height="80"/>
</p>
<div align="center">
[![Stars](https://gitee.com/py2cn/pyminer/badge/star.svg?theme=gvp)](https://gitee.com/py2cn/pyminer/stargazers)
[![Platform](https://img.shields.io/badge/python-v3.8-blue)](https://img.shields.io/badge/python-v3.7-blue)
[![Platform](https://img.shields.io/badge/PyQt5-v5.15.0-blue)](https://img.shields.io/badge/PyQt5-v5.15.0-blue)
[![License](https://img.shields.io/badge/license-GPL-blue)](https://img.shields.io/badge/license-GPL-blue)
[![Platform](https://img.shields.io/badge/python-v3.8-blue)](https://img.shields.io/badge/python-v3.8-blue)
[![Platform](https://img.shields.io/badge/PySide2-blue)](https://img.shields.io/badge/PySide2-blue)
[![License](https://img.shields.io/badge/license-LGPL-blue)](https://img.shields.io/badge/license-LGPL-blue)
</div>
@ -19,33 +19,63 @@
<a src="https://img.shields.io/badge/QQ%e7%be%a4-orange">
<img src="https://img.shields.io/badge/QQ%e7%be%a4-945391275-orange">
</a>
<a src="https://img.shields.io/badge/QQ-454017698-orange">
<img src="https://img.shields.io/badge/QQ-454017698-orange">
</a>
</div>
<p></p>
<p></p>
<div align="center">
`PyMiner` 是跨平台的,基于数据工作空间的数学工具。
<h2>pyminer</h2>
<h3> 开源、友好、跨平台的数据分析解决方案</h3>
</div>
PyMiner一款基于数据工作空间的数学工具通过加载插件的方式实现不同的需求用易于操作的形式完成数学计算相关工作。
pyminer 是一款基于Python的开源、跨平台数据分析环境。它以方便Python初学者为己任在Python的知识理论和工作实践之间搭建桥梁竭诚为初学者服务。
它开箱即用,大大减少配置解释器环境的繁琐性。不仅提供了编程运行的功能,还能够以交互式的形式进行常见的数据分析操作,减少代码编写和文档查阅的时间。
同时pyminer将提供面向新手的快速入门教程教程正由开发团队编写中。同时我们诚挚希望与Python培训机构合作让我们的产品帮助到更多学习Python的人。
pyminer通过加载各种插件实现不同的需求开发者可以通过编写插件将pyminer扩展的更强大、更趁手甚至创建一番自己的商用程序。
<p></p>
<p></p>
## 🔈 声明
pyminer 遵循LGPL协议是一个开放、包容的开源项目项目中的工具方法来源也非常广泛因此允许并鼓励任何人在遵循LGPL协议的基础上
1. 将pyminer用于商业、培训等任何合法场景
2. 复制、修改 pyminer中的任意代码且无需声明
3. 复制修改 pyminer官方文档
4. 鼓励自行写作 pyminer 相关的书籍、博客、文档等内容(收费也可);
5. 鼓励播主、培训机构培训 pyminer工具的任何内容收费也可
6. 对于商业应用的技术咨询pyminer团队保留服务的收费权。
pyminer希望成为一个伟大的开源项目也希望得到大家的认可和赞美仅此而已。
<p></p>
<p></p>
## 🎉 技术说明
项目开发环境支持跨平台windows,linux,mac 都支持如果使用出现问题欢迎提issue建议使用Python3.8+PyQt5.15基于python虚拟环境进行开发。
1. 项目开发环境支持跨平台windows,linux,mac 都支持。
2. Python版本支持Python3.5及以上但建议使用Python3.8及以上版本,性能更好。
3. Qt的Python接口使用PySide2版本为5.15.2。
4. 项目开发环境使用PyCharm
注意:
- pyminer 的官方发行版本为Python3.8+PySide2-5.15.2。开发者可自行使用其他版本的Python解释器配置相关环境。
- pyminer 曾经由PyQt5开发。但考虑到官方支持以及许可证的类型我们已经迁移到了PySide2并改变许可证为LGPL。请勿使用PyQt5安装。
- 当使用Python3.8配置环境时不支持3.8.0等低版本的Python3.8解释器。当使用Python3.8时请使用3.8.5或者更高版本的解释器。
- 如果使用出现问题欢迎提issue。
<p></p>
<p></p>
## 🎁 文档地址
[https://gitee.com/py2cn/pyminer/wikis](https://gitee.com/py2cn/pyminer/wikis)
- 项目文档:[https://gitee.com/py2cn/pyminer/wikis](https://gitee.com/py2cn/pyminer/wikis)
- API文档[http://py2cn.gitee.io/pyminer](http://py2cn.gitee.io/pyminer)
- MATLAB与Numpy对比[http://mathesaurus.sourceforge.net/matlab-numpy.html](http://mathesaurus.sourceforge.net/matlab-numpy.html)
<p></p>
<p></p>
@ -67,70 +97,91 @@ PyMiner一款基于数据工作空间的数学工具通过加载插件的方
<p></p>
<p></p>
#### Windows安装
### 发行版下载仅Windows系统
我们为Windows系统的用户提供了发行版的下载链接你可以在我们的官网中下载发行版即刻体验。对于Mac OS和Linux系统的用户暂时不提供发行版可以参阅“开发者自行安装”一节。
官网链接:[请点击这里打开](http://www.pyminer.com/)
### 开发者自行安装适合Windows、Mac OS以及各大Linux发行版
#### 安装前准备:
1. 确认你的Python解释器版本。pyminer支持3.5~3.9。
- 当使用Python3.8.x时建议x>=5,也就是使用Python3.8.5及以上版本否则安装PySide2可能遇到问题
- 3.5.x/3.6.x/3.7.x/3.9.x下由于开发人员不足未进行充分测试。为稳定起见建议解释器版本x>=5。
2. 建议新建全新的虚拟环境尤其是当旧的虚拟环境中存在其他依赖于PyQt/PySide2的程序时。pyminer与这些程序可能发生依赖冲突。
#### Windows安装 pyminer
```bash
#第一步:下载源代码
git clone https://gitee.com/py2cn/pyminer.git
#第二步同步安装依赖包和PyMiner如果遇到安装失败的情况需要手动安装
python -m pip install -i https://mirrors.cloud.tencent.com/pypi/simple -r requirements.txt
#安装依赖 (如果下载太慢,请复制源码目录下的 pip.ini 文件到python安装目录下)
pip install pyminer
#第三步:运行主程序
python app2.py
在控制台输入 pyminer 回车即可打开pyminer
```
#### Linux安装
#### 虚拟环境安装 pyminer
```bash
#第一步:下载源代码
git clone https://gitee.com/py2cn/pyminer.git
#第二步同步安装依赖包和PyMiner如果遇到安装失败的情况需要手动安装
python3 -m pip install -i https://mirrors.cloud.tencent.com/pypi/simple -r requirements_linux.txt
#第三步:运行主程序
python3 app2.py
#第一步创建pip虚拟环境
pip install pipenv
pipenv --three # 使用当前系统中的python3创建环境
#第二步:启动当前目录下的虚拟环境
pipenv shell
#第三步为虚拟环境安装pyminer
pipenv install pyminer -i https://mirrors.cloud.tencent.com/pypi/simple
```
## 🌻 项目成员
## 开发重点2021年4月修订
pyminer项目现在的开发目标是打造初学者友好的Python编程环境方便更多的人与Python这位踏实可靠也有点笨呆呆的自动化助手相知、相熟。
<a href="https://gitee.com/hzy15610046011" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAAFB0lEQVR4Xu1aaU9bRxQ9rAnBmLIaiiG4doIxYAPCrAWrlFZtkCr1Y39Yf0Jb9UNVqVWSpiKhQMtiUwwpEArEgbAYs3shbAGqO8bWM3Hwe+Y9U4mZb8Z3nu+cOffMufNI+va778/AB5I4EEEWcCDOq4EDwYGIFEbOCM4IzoioZoGXBi8NXhq8NC7rJLhGcI3gGsE1gmuEiPsWRcUyR61Gfk4OFlZWcPz2rYh0ri9EUSDqKithNdcgOTkZO14vBp1OLLnXJK02MyMDR8fHigOpKBBfdnRApy1hC9/2evHTk99RmJeHrtYW0AKljMOjY/Q57JhffC1lmuhYxYDQ5OXh84/bkJWZyZJ58dKF3pERlGg0cQGxf3CA3hE7KzMlhmJA1FeZYK2uRkpKCg6PjtDvGMXc4iIKc3PRXGtBxu3bMdeTlpqGrMw7SEpKgn9vD08G/sT69nbMefEEKAbE111dKC4sYDm51zfwc0+P5Pw+aWpEpV7P5m3t7uLHR48lP0PsBEWAMOn1aKmrw630NJycnMAxOYmxqWmxOYXjvurshLZIwz6TyP7a2yv5GWInKAJEt82GuyUfshx2fT487OuH1+8Xm1M47pvuB8jJzmafp+bn0Wd3SH6G2AmyA2G4Wwab1Ypb6eksh+U1D3559gzCRYlNLlYcHck/PHwUK0zU97IDIaTzjQWipuI+mi0WpKWmhnchxIgv2tuRnaUK/z01JYWdBldxnF5/AL8NDIja8VhBsjGC7PQDWweys7IifjMEhPCPFNNt68AHajUD4l/XK/SPjsbKVdHvZQOCzJOhrOydZKMB0WiuQb3JxKw3eYw+h0MxxygWPVmAaKiuQn1VFYjuNM7OzhjthRoRSogc52dtbVCrgo7z4PAQe/v7YvNlceQyh8cnZDVXsgAhLAvaYXKB1HVGA4LKp7wk2H/EOwi4nsEhrHg88T7inXmyAEFPba2vQ829e5hdWGT9RcgICUujyWJGrdHIbPdVxv8aCGqmPirVMspS13kRiCqDAc21tcxtMqZ4PHjc1x8+NYTN2OnpKcamp2F//s9V8JI0VzZGCH9V6CWIEU+HhlgnWlwQ7D0Cb97gjxE7Xrvd4Wk3AghyluQtyGPotFr8PTXFLLNw3BggYvH0RgAx88rF+g+h44wFjJjvo3kUMfOixSREIzgQ593nxMwMyHSlCnqQi7uiVqkiGEP3GL5AAKdn7//vx7XNTdla84QwgsTyshHNX5A7nV1YwNOh4XjZLmnetQNx0V/Q0Up3GaQnxIrply4MJKAhu1YgmsxmmI0V4ZIge/7XmBMlmkLcLy9n/QqVxqrHg5GJ5/BsbUnaZSnB1wJEaXERmi21KMgN9iM06L3F8Pg48xfEBrq/oLjQoJc8rqUlOKdfYMfnk7JGUbEJBYIudU0GA/Jzc5B83p2GnCZZc9KE0CAwOpuboCstjYglhvj8frg3NrC46mbgyDESAsTmzjYq9YZwnxFKnDTAtbyMwTHne1txi9GIOlMl7kR5DyJk0VXBSAgQ1Gt82toCrSZ4NU9NFR19ZLXFvAsldpCWVOh0yFapmHbQqTI5Ny+bkCoChK3RiqL8fLbo0FlPFzLtDQ3w7QUwOTuH1fX1uDaR3pTpSrWgOxDSC7kEVBEg4lrhNU/iQJxvAAeCAxFZi5wRnBGcEVHPJ14avDR4afDSuMy8co3gGsE1gmsE1wgRLT4XSy6WkTT5D8Yz8d8Z2/SVAAAAAElFTkSuQmCC" height="40"/></a>
<a href="https://gitee.com/py2cn_admin" target="_blank" margin="5" style='margin:5px'><img src="https://portrait.gitee.com/uploads/avatars/user/100/301972_py2cn_1578919767.png!avatar60" height="40"/></a>
<a href="https://gitee.com/junruoyu-zheng" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAACXElEQVR4Xu2YS0hUURzGvwuZMysxCSKIQEFhUJFMFy2SQEUYY9QBUQtsTBepjELiC0Q0H2DgA12UGQxoEGUQuAiCiKBcmERggohKvjEZJRIfV5iRexoXg8Nw4A4Xxe+sZg6Hw///3e/73XOPEn87xwsOKBTivwsohC8NFIJC+IORjqAj6IiAhwVGg9FgNBiNYF8SZAQZQUaQEWSExH0LYUlYEpaE5ZmBZUttBfKs6aLejU03Gjv6MPFjSgJ1+pecKlhSCN8DpRAUwj/bdAQdQUcEfN8xGowGo8FoBDsMkxFkBBlBRpxrRuTbsuAosCHqUiRW1zeQ+6DqfDqioqQQD4vyEBZ2AUsr6yiva8PiytoJMdoanLBl3RHz87+XkVPs1H/jIrmDIRczjsJclDsKYAq/CPf2XzR3DeDL+OSJEl/2tCL1RoKYn11YhN1RLdmG/mWGCJF26yZaaisRFRkBj8eD0bGPeNL93K/6REssnjbX4OqVy2L+87cJOBs79XcouYMhQmi1uPrbkZxoEWX929mF6/V7DA6/Ff9TkuLx+FExLHExUBQFB6qKZ643GHr1TrIN/csME+Ke3Yqqsvswm02iaq/Xi/0DVfsBkylcCHA8pmfmUFLdhN29ff0dSu5gmBBaPfXOUtizMwQrAg1NHA2SHb0v8P3nL8kWQrPMUCG0ku9mpqHIbkX09Wsw+5ygqof4497Ch09fMTQyaqgTjmU0XIjQPL/Q70IhfJpSCArhHy86go6gIwK+chgNRoPRYDSCnUfJCDKCjCAjyAiJr3bCkrAkLAlLwlIClkc2rHwEe1AdUAAAAABJRU5ErkJggg==" height="40"/></a>
<a href="https://gitee.com/lvvl" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAACX0lEQVR4Xu2bz4vTQBiG30mbND+6UfGi4EFFEK8rsqAuKx7EP9M/QzytKMIiiuJJFPcoiKLbbdOkaTIyk7jt2rSpwuI68861Lc08fM/7fTOl4uujAwkuCIKoqoAgahsIgiCOByMrghXBimgcFqgG1aAaVGPVSYIZwYxgRjAjmBFr3LcwLBmWDEuG5T8Ly/hBBPdiR3+/zIHkVYr0/WSN6ALcCx30t0M4oQBKYPwuQ/ImW+uzf/OmEw3LeRDq4YofJQ53ExQHZeuzGg0CEsj2cwyfjS0H8QeKmF0RdR2so4ixIMpEwvEF4KiyALJPOYbPlytiLIj8cwHhAd3zdReZAMnr5V3EaBDphwn6Wz5ET2hJiu8lDp82dxGjQQyejBBt+fCve4BioRT5mGP4YlER40GILhA/jOYUkRi9TDWQ+WU8CLXZ3jUX0S0fwpspMthNUA5mg5YVIBSM/u1AA1mmiDUgnNhBfC9E55zqp4DMJIZ7KSb7lSLWgGhSZPqtwODxCHJqGQityN0AvaszRdTpdLSX2lURCkTnjIONnUVF5Lg08xiuJks1RzQtNVeEm6qLVK9OvxRI3mbo3wnMu49YBUIrsh2gd6VWpKwGLfdS1z4QWhHVRc7WXURfZMlq1jDphqqtItS2tSI3fQj3N4FsA7GgyC8eNoJQisT3Q6iB62jZCEIrcsNDtOkD3RqFrSDU9jd2AniX67D430G0XlWfojec6O8ap2ifrY9CEDUigiCI47awIlgRrIjGDkI1qAbVoBqrxktmBDOCGcGMYEa0HsL53/AjROwa7BrsGo2J8RPW2YlZzjPz/wAAAABJRU5ErkJggg==" height="40"/></a>
<a href="https://gitee.com/panhaoyu" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAAE10lEQVR4Xu2bXWwUVRTH/zM7Ox+7Cy1UsN0KLdvWokCgPhljIjEmJmIMMcKD8kCqBA1qIIaIsWoJxhQFwe/PGlBDNCaGF4gPxBCDiSYYopa0Jf0u21IqJaW7O7OzOzvm3lm3LF2adec6G5N7n/owO3vP7/7P/5xzNxWmz1TY4AsCB+GogIPIZgMHwUHkGyNXBFcEV0TBZoGnBk8Nnho8NeabJLhHcI/gHsE9gntEEfct3CzLZpa+ELTIAViJHpjRw0WclTePeKsIXwiB5k5Iix6g0VnTZ2AM74M1c9abaOf5Fk9ByOEdUJbthiBV5rZkm1EYIx1ITXxVVhiegiCRSpX3Q13xBsRAM7kpdILPGEhNfgt98GXAipUFiOcgaJTEJxrfh7/qYUCQsoHbsGZ+gzG4pyypUh4Q2dCV5W1QwtsB34KcCjLGAIzBNqSnTnqqjLKCIJH6l2yGWr8Xglwz6xupSRhDe5G6/LVnMMoOgmZKxb3QGg5C1IhvOMuKnUOi+wnY5pgnMDwDodS9ClEOO0EmumFG38kLkEAINH8OMbgaGf0C9P4XaHn1ankGItTyC8TASgfE9GnEuzbOiZHAUOvaYE4cRfrqKa8Y0O9hDkKQw7Cta3PKYDEgbhY5SR2lZhukyvuQmjoF/cJTzCExB6HW74Mcfpr2Blb8PIzBl2i+uwERXHMSvoX30ODt5AgSva3MSyxzEFrTh/AvfZxuOpPoQezc3fRvNyCU23ZBWfYiIKqAnUIy+gGSw+1MVcEcRHD1cfgq1jteMPMr4n886BqEqDYgcOc3ELUmB3D8d8T+3MC0C2ULwhdCaO2PELXb6YZTf30PvbfVNQjyArXhbcjVWwGIFIAx9BrMS53MVMEUhFS5HlrTxxDkasBOwxz7CMbQK0xA+Jc8BjVyIDewkc4z0e2kIIvFFIRcsw1qfTsgBol+YQy1wxz/jAkI6jPrTkMMrvtPTJMpCDXyFuSaJ6l87dQk9L7nkJ76gRkIpyI94wxqGQPJ0f1IXjzEQhBs+4jrjZIMT4nzm5Ax+pmBkKo2Qms8BEFaRN/JMj2YKYI6+6rvIKoRp2JM/4R41yO50yqlfJJGyjYnkdF7Z9+Tlx7DSPRspX2K28UMBOkdtEgH4FtIChzM8U4YA7tdgdAa34VUtQHpKydo70CAaI3vwX/rFudSx7oGfWAPUpePueXALjXyytsNRkmNrohZ48ZotOYv4L/l0azCnPkkz5ALAC+VCBtFkP5hzQmIwbVZR58r2VJAXO85qYkvofc9j1yJ9i+GFe+COfYJveZzu5iAIAC0yH74FtwFCHLB6bIUELPl0oZ56SiM/p00Xrl2J6zYWaZjOhMQ/5wGGaOV2h3OSY1/mndI/xaEL9SCwMojEJQ6MmkhOXoQydE33R78TT/PFMR8uwy1/AwxsIo+UsysQC5ylNpnqcLs9FXofbuQvnL8/w8icMcxSIsfmg0ko9M2vOASREAM5K77iwHnlpBnivAv3QJ1xet5P+4UtXnSQUYPIznSUdTjpT7kGQhqctWtkMPbISrLAVGbf892GrZ5EcnxI578RuopiFJPy4vPcRBZyhwEB5GfcFwRXBFcEYV7OP7PbQ4X7hHcI7hHcI+Yr1XnHsE9gnsE9wjuEUVcaHCz5GaZL5O/Adf8pacTFVJYAAAAAElFTkSuQmCC" height="40"/></a>
<a href="https://gitee.com/892768447" target="_blank" margin="5" style='margin:5px'><img src="https://portrait.gitee.com/uploads/avatars/user/24/74606_892768447_1578916388.png!avatar60" height="40"/></a>
<a href="https://gitee.com/fireinice" target="_blank" margin="5" style='margin:5px'><img src="https://portrait.gitee.com/uploads/avatars/user/2457/7373699_fireinice_1585479305.png!avatar60" height="40"/></a>
<a href="https://gitee.com/houxinluo" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAABM0lEQVR4Xu2aMQrCQBQF3xIUxBOIJ1BB8BxCGi9jIwgewdY7WFtZWVjbCtY2NlYhxAi6CoElLgmJEMY6S8jzzex+jRksd6n4yBDEuwUEYWkgCILIipFG0Aga4TwsgAZogAZo5E0SOAJH4AgcgSM8fm9BlsgSWSLLv8lyFY40m/Rf97/eI823Jx0vNw91SeG4p8V0qG47UJyk2hwuWu/PXmuLXFSpLAnCfiUEQRBZOmkEjaARzh0LNEADNH6iUeS091nTqJMlQdhZI0lTRfFDvm+lGCN1WoGMxKzB0CWmz69KGMNtFARBENkNlkbQCBrhPHSCBmg0GI0yg1bdayv9X6PuhylzP4Kw6REEQWRBohE0gkY4NxfQAA3QAI28kyeOwBE4AkfgCI/5HFkiS2SJLPNU8QThDTljz/nugQAAAABJRU5ErkJggg==" height="40"/></a>
<a href="https://gitee.com/fzgandw" target="_blank" margin="5" style='margin:5px'><img src="https://portrait.gitee.com/uploads/avatars/user/2661/7985556_fzgandw_1598515660.png!avatar60" height="40"/></a>
<a href="https://gitee.com/olxjdqj" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAABEklEQVR4Xu3YParCQBQG0Cu4BTcguDs38Jpn5WasFBSyigcPXIN9QPzHCJomEMKMRbQ41jMQvtzvOJnB7ndchV8MBPGaAkHUbRCEIJowmggTYSJaDwuqoRqqoRpdXxKMYAQjGMGIhPsWWMISlrD8CixH0yKGo8nzWQ5/iyhXPwmE9bekNywFUb9UQQii2W8TYSJMROt/nmqohmqoRteBmBGMYAQjGJFwbQBLWMISlrDMxLK6niLut4RdEdXlEGUxj9N2nbT+3UUfubzNedjqvI9yM4vj/zJnW/ZaQdSR9RZE9ivqeYMgTERz5EyEiTARrQyrhmqohmp0ndEYwQhGMIIRCV+ysIQlLGEJS1gmJFAveQCBfEScIengwAAAAABJRU5ErkJggg==" height="40"/></a>
<a href="https://gitee.com/wfifi" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAAGmklEQVR4Xu1ZaWxUVRT+Ztrp0H1f6CYKaQlGwVBJXYCAKCEW454QgyQGNUQNJmgiEdM0YDBRjGsQQzTIDxJRMVLToChCxVSoAaIi1GK6033fp50x97b3eebNvfe9/jV3fnXm3XvfPd/5zneWeko++jsE84HHADHDAgPEbDQYIAwQ4cJoGGEYYRghLRZMaJjQMKFhQkPXSRiNMBphNMJohNEIF/MWI5ZGLI1YGrF0JZaHHynA4gw/X3upYxxbv25xlJiDD+ZjafY8a9251jE8V9nquO/IY4VYlBbD17E944EgVi2I59+bBwJ4saoNTQMB7Tmvr8vBfQsTrDXX+ibx1LFmjAb0s+j3789FaX4c30f3WGJJD24bmsLO76/jcteE8jIlubGoWJuNrPhoa42bfStviMeu1VlIi43CdBA48ns/37/plhREeYGBiSD2nunED/8MK98d5/Pgk4cKsDB1Bkz2cbOvMNmHdzbkoiDZx/d8d20Yr55s539bQDy5NBXPlKTBH+3hqO4724Vvrg4qL0PXi0VjUyF8UNONz/8ccLVPXJ4t3rkqC8l+LwLBEA5f7Mf+8z3KM+65KcFaLxYJUN+t6Xa1b2IqhI9re/HZpb5wIKiH3RwqGMTWtg4FwNBmpDx+ZRC7T3cqL1O+JhtlRYlh1GRfqIepp2QHbS/NsBjU2B/gHvZ6ZsJMF5rbbk/H5mUp8Hk96ByZQvmPHahtGwsHgn2jOlHTMooXvm2TGkSpOTIZ5DTeUJTIX3ClewKbv2xWAkHfQQ3et36+pRP1vZPYdLRJecaHZXlYkRfL2VNVNwTGkPgYL5xCk77DroNhBdVrq7OwcXESPAAa+gPY8lWTVHwoNdnLD5zvwfY7Mnjc945NY8/pTlQ3jkQYsiI/FhVrcpARFwU7NZ8tScOW21I5mN2j0yg/1Y5zLTPeop8lmX7svXc+chOjuS68fbYLW5encVboQpM579DDhViQ4kMwBHxxeQBv/txlHR0GxOM3J+P50gzERnu04kOpKegoPM28dOhCHw7U9kYYQXXFbiwVUTtI9CB6R6H6u9fmcDbpQpM6T6aBYUAsy5mHPetykB0fjcnpED690IeDv0UaJKhJtYTG/pmGEew4cT0CCMo4WQiJtMoMOvbXIM8e9g89Q4QWjX1VaFLGyUIootegtYFMtOzUFKmOeltVC4izVZ6jKVxVywjmUdZQb6tCk54tE9UIIF6+OxOPLknmKixDV0ZNRjWadZiAvvVLFyqvDlkOpWxTxTIFU+Y1Gj5U9al4q0JTsE2VESOAKCtOxEt3ZnIVtqcYZpWMmsJa4XGZGFEAVeruBCalt50x1OP20KQAqgqvCCAo9WWek1FTAEHZZL8ofabL9+J8medE+pMBrQtN+kxVikvnEUIM7S9UUVMAoWPT/o15PHycijXKOOpZWh7LQk/HJirkqmJNCgRNj6zy2nZ8ppHSUZM9p5elYqYSWFnFREOI1jIUZCcxtjtQx2JxBykQ6xcl4pWVmUiI8YZ1gyIOZdQUB9LqrbJuCBWnOuDGCLGfiirNANQ5qvRMw084kDJFV+xJgaCeHZ4M4o3qLlQ3Dlv9gIyawhDKGpF16AVVRlB2CNGltQwtq1UFGwVcCHJJbpzVTOrKf+XMUnhWdIN1PRNWx6ebGdCc3jEyhV0n2/F0SbrVG6iMoEBQ4Birjv7Rb5XVOq/SEBQOZD0JaxvYR9cQKoGg1RrzIhuUiJmB24aM1Rfv1XTjiVtTeC+gM4IC8UBxEnbclQlWHzAvMgNE6T+Xhoy188vzYvnwyGlEoASCepa9vG0wwOt5J9VnBtEWvap+CCzbsFmDU2cqwKCeZaz6tWUUZUVJvMibS4v+U8MIitL9vEFz6kyVQNBqjRVWgSCQN9vxOU2QaN6u65nEjak+RHs9jrMKygqhCYzibN5RnO6P6FhlWYc6kGUdNkFjtjjNKrT/16CenQqF4I/yhM35ZBdhv1GlHpwIIsnvdaSm/SyRJdjv4t2ySte+jzqQhaYvCvDCw0eCuumVFgiaAcQLnahpL7fFdyGcF9vHVfiF/U4zgHjgdqhsH+wK4TxR/1/vY7+EFghaSbKNujmB/WCq/OyZWyPEOfZBq652sb/bPk91Mxl3/JcfHa25oaa4FPXoXIygRtHiTFe72IGwT9jd1C6OQLji8f9gkQFi1okGCANEeDwbRhhGGEZIc5wJDRMaJjRMaOgKYKMRRiOMRhiNMBrhYkxgxNKIpRFLI5ZGLF2I5b+O07VathbMeAAAAABJRU5ErkJggg==" height="40"/></a>
<a href="https://gitee.com/BigBossQzz" target="_blank" margin="5" style='margin:5px'><img src="https://portrait.gitee.com/uploads/avatars/user/1760/5280558_BigBossQzz_1598407750.png!avatar60" height="40"/></a>
<a href="https://gitee.com/gavinee" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAAG4ElEQVR4Xu1aaWxUVRT+ptNlBoYpbZluFLpBCxJkKbIviiKLFQPWslkVQiQYCBaiKEjAgAgmQhCUmBgKElG2QrBYI5GCCpbKZlhK9710X6admU6nMzXnljZv2mk70DuVH/f86uS+d9+53/3Od849t7Jnr8c2QxhkAogWFgggHkWDAEIAYS2MghGCEYIRNosFERoiNERoiNDo6iQhNEJohNAIoRFCI+zotzhULA8OXYVRqiBkGUrwW/VtnC5Pht5itOnWfK/nML7fUDaWaXiIw6VJdrjP7xGHAbHafzaW+86Em8wFpmYzjpQmYX/RL516vjVwERYOmMDGr9Vl4N30gx2eDVH4QC5zQobhIT8EHJk1pqiHYWtQNHxc+sMMC5Kq72JL7o+YqA7DCt+ZCFX64ljZn1bAdAZEHyc3RHpF4LUB4xGuHIg6swH7ChNwtjKFKxjcGUG7tjskBmFKfzSjGde0GYjNimNOfxIYhTmeYyCHExosJnxfeglfFyeyMSkQN+uzkW4oxhhVCIIUGsYqqZU01mBn/mlcrr3HDQyuQNDu7Q1djgnqlli/VZ+D7Xknkd1Qyn5Lx2WQtexu0XmcLL9qBYSt1RGzyhprcaM+G2cqknG9LosbCDQRNyAmq8OxLiAS4Up/0CKNzSbozY1wdXJmDtOuOsucOjhPO78x+yhifJ5v0wh6iBZe12RAuuEh/tam4VLN3TZAuSLAWyM2D45ClGYSnCB7LD8pfP6ovY9KU30bEBQaBE6ZqbbDXGNVIVjiPRWJVbdwsebOY32rq4e5MWKu51hsGrwQankf9r2mZgtjRb3ZgNomPapM9Sg3aaE163FfV4AJ6jC84hXBgCswViJVX4jZHqO7zBo0OMtjFLYEvgG1XIn7+kIsTd3LBQxuQFD8v+c/BzkNpUwbWnWhMy8D3TTYMGg+i3USTalY3tHlY31WnE1GbBy0ANGayXCWyXFVm4bVGd8+XUC0ehM/4kOEKnztco6ovznnB6TUZWKV38tY6fcSXGUtmtKdNTWbGYAktjyMGyN6CgSl3V0s7foxse3OWkW2O+Z1N0/ruMOAoFL6cEkSioyVVr4MdPPCO74vsFQqZcSIPoMQrPCGr6sHghTenfpvgQV3dflIqLzRablu7+KlzzkMCJ3FiJ15p5BQdcPKr0jPCGwKjELfdkDYU2ITeCq5wqZ2PMniewUIexyTMqIrIMb1C8WCARNBtQplmQPFiawI42kOYwTVB1RGW2D9Xwe0EIWTC9OBroA4U3GNpcoxqmB4OPe10g1pRcoLDIcB0ZPQIPBsFWZUbeY2lOFsRQrLGDztqQRCukBHnzEcnjXs2a3OQoPYRD2HFG0GEqtudluc2fOt7p5xGCN6qhG2GjOtizk2PBY6sxEXqm9zS6MOA6InGtFZh4qAoObOjqCl0LioUdVUj225x7n0JXoFiGnuw+H+6DAWrPTBMu/pUDq5WmWNjwcvRLRmChNJg6WRFWLtMw4BoZIr4efan2WRnIYydujqrA/aXTj0Sh0hZcT+ISsx3f2ZDn5lNpQgJnUfWwiBEBsQySpOe4xASqi8zlqAPKxXGLFYMxXvB0QyFpBRfVFgrEBcyUWcl1Se6wNexTzPCHg4q2w2cehd0h7Sh5S6DHyeH8+tyuQORGsYUD+CGiy2mis8dpD3HNyBIAe3By1hVWFxYxWStek4UJTYIY6pH5FnLLdaD4E4SR2OB/oinKv8h41RqLyumQhq/NA7P5X/1eW1wJMCxB2IkX0D8UXIW/B39WA+URtubeZ37O8NAfOx1Hs6o/1tXS7efvBVm99x4WtAbTgy6lYtTt3D/qb5tgZGY6jSj/12RHlN83IHgtgQ6TWOqT85vafwZ8RXJLNFSJsvRY1V+CDrCO7pC9gYdbdW+L4IF5mctfN25sezYopsZv+RrD3n6axiv0tNNfg09wSuaB88KQE6vMcViPYOS9lAX5YewWvNetbqv1D9L3NqfL8h+Cx4Gbxd3Jkg0nliW97xNofXDpyHGJ8ZbXccJJbrMg9xSZ1cGSG92KGJSSSp2JHu2gz3EdgWtIjtLKXM3flnrG6srMOjCCvSDlgtlO5SJ6nDWA1BrTq6Lfuy8BwXVnBjxK7gNzHLYzSLf+peHy293EHUpLtO96GHSn7HN8W/ti1EWlRRx3tH/il2n9Fq0vfpMHZPV4CPso+Cwqynxg0IcoQaKEs001Bj1jHatzdqx1GbzmhpYgep9pc29P5k9TAka9NYQ9eWrfGfi8EKDU6UX+F628UViJ7uyv/5vgDiEfoCCAGEdSAKRghGCEbYTE4iNERoiNAQodFV5So0QmiE0AihEUIj7DjfC7EUYmlNk/8AnJ4R08wDRU0AAAAASUVORK5CYII=" height="40"/></a>
<a href="https://gitee.com/wudenggang" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAAE0klEQVR4Xu2Zb2xTVRjGn9u1naUbXec22MZgCImIMYgOXIjbwj8nCSSAxoQPSDREQ6bRRE0kYsiCZh+URBINShYI8IEEUIyMGBEhIJpFR4AYEFAMbqx06+zWlXZ0/UfOHbeee7337pbe6/rh7bem99xzzvO+7+99zqlQ9/kfadAHAgkxlgUkxL1qICFICDkYKSMoIygjVM0ClQaVBpUGlYbeSYIYQYwgRhAjiBEG7lsIlgRLgiXBkmD5f8Fy6UNF2NxYAU+hDckUcOC3IezoHNCdvmGGG1uaKlDqKhCfi6fS2Ht+EF90BXXHNc8uxrsN5Shy2gyPMaCDOZe30z0OfLKiCjUehzjnmRsRvPXdLd35X60rxYb5XjhsQuY5I+PeqC/DusdKUGADgiNJfHC6Hz/+HTGyV91nTGuf25sr0VjrFie7MRTHhq+6EY1r/2XCPy+tsCcUx5vf+tAdimsumh93ZSCG9V/25CwCe4FpQvARHi9SfAYNx1JIpNJiiURGU/j45wA6roZVNzfJIWDv2umoLXGASXz0yjC2ne7PLyF4Towm09hzfhDt59TrfeXDxXh7UTncThuuD44iGE1iQbULqTRw+HIIH50NqG6On2MkkcannQM4eCmUX0KwaO1eU4NZXqe4sI5rYbSe6lNd5DtPl+P5uR4wPDAusFKQ6v5i3x1s/Pqm6rhNCx7E+sdLRK74wgls/v4WLgdi+SUEW82Hy6bimVlF4sL06rd99TTMm/JAhvp/BkczXac/ksDWk33o8o38Z4M8H9jvm472miKCqYxgL9v4ZClemu+Fs0BAXySBLSf8uOC/I1tsXZULrUumoMJtz1D/nC+aqf1YIo1dXUHsuzgoG8dn3HgldD/qmAZLNjnvDbRq+MV5XrxSV4pCuyDLGj7ax6/fxnsn/LL98O8eD6oTLgRbwP7najCnrFATfFL5KKnPdx0G0JeP9MjaL59tRtpstmKYmhFs8vebKrBqzmQwm/RL7whaOv6tYz69lRnDRzwUS6HtTD9++Ot2Zj88f4wYrwkXgk99ZeT49qdGfSmb1Gy69JtRKz7hQvAwVEaWt8fKbNHLpoXTXGhdPBVlkwpMtdW8WKaXBns53x73XxjCzl//Eef8bGU1Fla7NA9mLzzqwWv1ZXDZ5T5BC7DZRl3veUuEYCfRNY+McULqALytVmMAW+Tc8kK0La9EVbFdBOX2nwL45uqwjDtqHcUMQSwRgo8sM0vrDnVDaauVXUHajJQ1vFeQ+KDlMfJWCD6yA9Ektp7yo6m2KGOr9aLK2+/Om1HxjkIyYHquM1cxLMkItqidq6rBwClFsXGmW7TV2RzI2HH+8KUhtDw1xg29c0jeCiFFVhCAY9fCeKLSJdZ+Nkd09izLimdnF4v71DuZ5q0QPBN+D8Qwo8QJZqiMXKZIdpuZLn84jple57h3FXkrBN8lYsk07IIAm83YZYpkt9nm4kmIAlphqy33EcoOIH03epmivNhl41mJvH7Ml2vgNcdbBks2I+8k2fdsLlOklsnGGb0Zz0UlS4XgzxZskWq2Wmvx/OFNy4DlsnHlWEuFMHOhVr+LhLinMAlBQsiLjTKCMoIyQrUBUWlQaVBpUGnouVNiBDGCGEGMIEYYOMMTLAmWBEuCpR4q7gJAqTY8I+99qgAAAABJRU5ErkJggg==" height="40"/></a>
<a href="https://gitee.com/py2cn_admin" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAABOElEQVR4Xu2ZMQrCMBSGX2/i5uwFPIGjB9BzuHoCV++gm5O6OnZTEGcLnQQXwWoFDdJgDG+oScGva0Ob/vn/L695SXs2KIVLEoR4uQAhTBoQAiFsMOIIHIEjnMUC0SAaRINo+P4kYASMgBEwAkYozluAJbAElsCykbAcd4bSb3U/5na9FzLdL2SymysQV9+QaLBECLOIVSHyy0k2+fZ5pyhvsspSWWdpfcuteFIjHHE4H6W3HCmm+7shCBG7jqhGA0eYXQMhEEKEaDi2T6JBNIjGuyKCETDCLo9xBI7AEc5fRqJBNIgG0fCdJ8EIGAEjYASMUJy6A0tg+R2WdLpoAttHdVWv/F03XMHToEOi9T6DfqXiZQgRuwmsWKSgQ3AEjrANhyNwBI5wQphoEA2iQTR8FRqMMOo8ACOBabofxKKyAAAAAElFTkSuQmCC" height="40"/></a>
<a href="https://gitee.com/nwljy" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAACX0lEQVR4Xu2bz4vTQBiG30mbND+6UfGi4EFFEK8rsqAuKx7EP9M/QzytKMIiiuJJFPcoiKLbbdOkaTIyk7jt2rSpwuI68861Lc08fM/7fTOl4uujAwkuCIKoqoAgahsIgiCOByMrghXBimgcFqgG1aAaVGPVSYIZwYxgRjAjmBFr3LcwLBmWDEuG5T8Ly/hBBPdiR3+/zIHkVYr0/WSN6ALcCx30t0M4oQBKYPwuQ/ImW+uzf/OmEw3LeRDq4YofJQ53ExQHZeuzGg0CEsj2cwyfjS0H8QeKmF0RdR2so4ixIMpEwvEF4KiyALJPOYbPlytiLIj8cwHhAd3zdReZAMnr5V3EaBDphwn6Wz5ET2hJiu8lDp82dxGjQQyejBBt+fCve4BioRT5mGP4YlER40GILhA/jOYUkRi9TDWQ+WU8CLXZ3jUX0S0fwpspMthNUA5mg5YVIBSM/u1AA1mmiDUgnNhBfC9E55zqp4DMJIZ7KSb7lSLWgGhSZPqtwODxCHJqGQityN0AvaszRdTpdLSX2lURCkTnjIONnUVF5Lg08xiuJks1RzQtNVeEm6qLVK9OvxRI3mbo3wnMu49YBUIrsh2gd6VWpKwGLfdS1z4QWhHVRc7WXURfZMlq1jDphqqtItS2tSI3fQj3N4FsA7GgyC8eNoJQisT3Q6iB62jZCEIrcsNDtOkD3RqFrSDU9jd2AniX67D430G0XlWfojec6O8ap2ifrY9CEDUigiCI47awIlgRrIjGDkI1qAbVoBqrxktmBDOCGcGMYEa0HsL53/AjROwa7BrsGo2J8RPW2YlZzjPz/wAAAABJRU5ErkJggg==" height="40"/></a>
<a href="https://gitee.com/tangtang666" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAABHUlEQVR4Xu3YO4qEQBhF4b8TXYzL0NjFmAiGrkvBVbgVE2Ga7jERfMEg3QOfqWVRXM89VvmIiJ9wxUMQvxQIYmmDIASxFiMiEIGIzc2CaqiGaqjG0UmCIziCIziCIy78byFLsiRLsvyYLNu2jbquI0mSC7o6HtL3fRRF8ed59ia4VZaCWGJvmiaqqtol4kVKmqbv0fM8xzRNu298GIYoy/J/EnG26q7rIs/z97BxHCPLsrNHbrt/azXOVi2IJSFBCGJdFkQgAhGbHxDVUA3VUI2j3SVHcARHcARHnJ3BI4IsyZIsyZIsL8jym4Z89OetIL4pgWUtiBDEGktEIAIRm6pWDdVQDdU42sdxBEdwBEdwxIXTLlmSJVmS5ZEqnjhi2AGX5AhBAAAAAElFTkSuQmCC" height="40"/></a>
<a href="https://gitee.com/dechin" target="_blank" margin="5" style='margin:5px'><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAABCCAYAAADjVADoAAAC0UlEQVR4Xu2YT0iTcRzGn70zKVthWVJzEVSrKPs7EoJEMoLw0KUunaNDlzoEHurSqUM3CyIQ6c8pqFskQbYKp1iWaeqWoo1NWzrX3NpwOLe98b66cLnGhv7eXtjzXt7D3r3s+9nzfN739zM0X2iVwQMGgphPAUEstIEgCCJTjEwEE8FEZH1ZYDVYDVaD1ci1kqAj6Ag6go6gI/LYb6EsKUvKkrL8b7JsuF4Dc3VFHqoCkvEU4rEEAu4wvtrH4Pk4mdf3VuoiobIsBETGQDLgHw2h67EL/pHQSs2a8z6agZCTMhLxZPZ+SgaUlBoBQ+bH0UAMjpZBjPdNCYehGYjQeBTPGtv/OdDajathra3CnpMWrNtc9gdK0BuB/W4vQt+jQmHoBkR6SgVI3eWDMO+rUGHIMjDa4cPbe33FBUKZtrzKhFNXj2CDxaQOHwvH8e7+F6EV0V0i0n/7obM7cPScFcZVkpoK1ysPOh86haVCtyDWbynDmcZjUM7KMTk8jec3u4oPhDLx6Ws2bLdVqsNHpmJ43fQZgW9hITB0mwhl2hMXq7G3fpsqzdnoHBwtA3C/nyg+ELbzViiukEokzMUS6HgwiBGHjyAIgolgNVQP1F46oL5yp1+q2pv74e3xF58jGm7UwLx/fhn/a2IGL293q2cRh24fn5W7ylF/5TBMm9aoc/sGfqL11gcRDNR76hbE4kdnKimj/4Ub3U+GigvE34suZV/CfqdX6CaN7hKxZBmekuFq8wpdcOmqGkoKdtdZsPP4Vigw0rtVQU8EbU09wiSZ7ppmici1VScZJRhLpSX9V54QnY+cQvchNAdRiOUUaD5nEJ+eDgv1wuLfpFkicoKQoW7szkzPqoMPvRnDD1ewEHbLvlYoiGX/Og1vQBALsAmCIDJ7x0QwEUxE1mcRq8FqsBqsRq4XVTqCjqAj6Ag6Io/lPGVJWVKWlCVlmYcsfwMrVQjz8fzYbAAAAABJRU5ErkJggg==" height="40"/></a>
因此pyminer的开发重点为
## 📱 联系我们
1、计算、统计方面低代码化功能性开发。需求详见[低代码化功能性开发](https://gitee.com/py2cn/pyminer/issues/I3HTG9?from=project-issue)
作者PyMiner Development Team
2、编写适用于Python入门的pyminer官方教程。需求详见[编写教程](https://gitee.com/py2cn/pyminer/issues/I3I7FW?from=project-issue) 。在教程编写方面,
我们深知开发团队力量有限,因此诚挚希望可以和各位开发者或培训机构合作。
邮箱aboutlong@qq.com
3、插件商店的开发。插件商店可以让pyminer如虎添翼。这一部分的开发需求详见[插件商店](https://gitee.com/py2cn/pyminer/issues/I1TWAR?from=project-issue)
为了减少开发负担样式表只使用浅色样式表Fusion不对深色样式表做优化且不再进行界面语言翻译工作。
界面语言直接使用中文。
## 📱 加入我们
作者pyminer Development Team
邮箱team@pyminer.com
欢迎各位开发者大佬加入
<p></p>
<p></p>
<img src="pyminer/resources/screenshot/group.jpg" width = "300" height = "500" alt="QQ群" align=center />
<p></p>
<p></p>
## 🚥 许可说明
本项目遵循GPL许可证。此外对个人用户来说本项目支持自由修改源码和使用但是不支持任何未经授权许可的商用或其他盈利性行为也不支持任何未经授权申请著作权的行为。如有违反以上许可本项目管理团队有权进行否决。
许可解释权归属 PyMiner Development Team。
本项目遵循LGPL许可证。
许可解释权归属 pyminer Development Team。
## 📸 预览截图
基本界面
![avatar](pyminer2/ui/source/screenshot/main.png)
查看变量
![avatar](pyminer2/ui/source/screenshot/check_data.png)
![avatar](pyminer/resources/screenshot/main.png)
代码提示
![avatar](pyminer2/ui/source/screenshot/code.png)
![avatar](pyminer/resources/screenshot/code.png)
绘图
![avatar](pyminer/resources/screenshot/check_data.png)

28
app2.py
View File

@ -1,28 +0,0 @@
import cgitb
import time
import logging
from pyminer2.pmappmodern import main
import ctypes
import platform
if platform.system().lower() == 'windows':
"""
无论是点击bat还是在终端运行如果你发现控制台窗口闪现一下就关闭了那么就是由以下代码造成的
注释掉以下代码即可正常显示控制台
"""
whnd = ctypes.windll.kernel32.GetConsoleWindow()
if whnd != 0:
ctypes.windll.user32.ShowWindow(whnd, 0)
ctypes.windll.kernel32.CloseHandle(whnd)
if __name__ == '__main__':
t0 = time.time() # 开始时间
# 异常处理设置
cgitb.enable(format='text')
# 日志设置
logger = logging.getLogger(__name__)
t2 = time.time() # 结束时间
logging.info(f'time spent for importing modules {t2 - t0}') # 打印日志,启动耗时
main()

6
docs/.gitignore vendored
View File

@ -1,2 +1,6 @@
/build
/source/alg
/source/alg
/source/pkgs
/source/pmg
/source/pyminer
/source/dev

7
docs/PyMiner介绍.md Normal file
View File

@ -0,0 +1,7 @@
# PyMiner——Python数据分析的利器
对于Python初学者而言从安装Python解释器到配置专门的代码编辑器无疑是十分耗费精力的。
PyMiner是一款使用PySide2编写的数据分析软件内部集成了数据分析的常用包并且将常见操作封装为图形界面的形式提供低代码的数据处理交互。
在线文档可以看这里:
[在线文档](https://gitee.com/py2cn/pyminer/wikis/%E7%AE%80%E4%BB%8B?sort_id=2761029)

View File

@ -1,56 +1,114 @@
import os
import shutil
import sys
from os import path
# 文档的生程流程如下所示:
# 首先,根据*.py文件生成一系列的*.rst文件然后根据*.rst文件生成html文件即静态页面。
base_path = path.abspath(path.join(__file__, '../..'))
sys.path.insert(0, base_path)
# 是否将文件上传
# 在进行页面上传时,会开启所有的强制刷新选项,这会降低页面的生成性能,但无伤大雅。
upload = False
# upload = True
from sphinx.application import Sphinx
# 项目的根路径即pyminer项目的根文件夹
base = os.path.dirname(os.path.dirname(__file__))
sys.path.insert(0, base)
# *.py文件夹在这些路径读取*.py然后生成*.rst文件
alg_src_dir = os.path.join(base, 'pyminer_algorithms')
# 用于存储rst模板的文件夹这些模板用于生成*.rst文件
template_dir = os.path.join(base, 'docs', 'templates', 'apidoc')
# 根据*.py生成的*.rst文件夹
alg_rst_dir = os.path.join(base, 'docs', 'source', 'alg')
upload and os.path.exists(alg_rst_dir) and shutil.rmtree(alg_rst_dir)
# make文件的路径本脚本调用sphinx的makefile实现功能
# TODO 目前仅支持调用windows下的makefile请linux用户自行优化脚本并提交
make_path = os.path.join(base, 'docs', 'make.bat')
# 最终生成的html文件夹
html_path = os.path.join(base, 'docs', 'build', 'html')
from pyminer_devutils.doc import RstGenerator, PythonFileTreeNode
def cmd(command):
for line in os.popen(command):
print(line, end='')
def main():
# 在这个文件下定义了文档的具体生成细节包括rst文件夹的位置、排除的文档等。
base = os.path.dirname(os.path.dirname(__file__))
source_dir = path.join(base, 'docs', 'source')
rst_root_dir = path.join(base, 'docs', 'build', 'rst')
path.exists(rst_root_dir) or os.makedirs(rst_root_dir)
generator = RstGenerator()
generator.sync_directory(source_dir, rst_root_dir, rm=False)
# 生成算法部分的文档
py_dir = os.path.join(base, 'pyminer_algorithms') # py源文件的目录
rst_dir = os.path.join(rst_root_dir, 'alg') # 根据py生成的rst文件的目录
exclude_dir = [os.path.join(py_dir, p) for p in (
# TODO (panhaoyu) 以下内容为无法自动生成的部分,请各位开发者自行检查其中的问题
'plotting/graph.py',
)]
node = PythonFileTreeNode(py_dir, exclude_dir)
generator.generate_python_package_files(node, rst_dir, 'pyminer_algorithms')
# 生成界面部分的文档
py_dir = os.path.join(base, 'pmgwidgets') # py源文件的目录
rst_dir = os.path.join(rst_root_dir, 'pmg') # 根据py生成的rst文件的目录
exclude_dir = [os.path.join(py_dir, p) for p in (
# TODO (panhaoyu) 以下内容为无法自动生成的部分,请各位开发者自行检查其中的问题
'display/browser/get_ipy.py',
'widgets/basic/texts/webeditors/__init__.py',
'examples',
'display/browser/handler.py',
'widgets/basic/quick/demo1.py',
'widgets/basic/texts',
'widgets/extended/texts/texteditor.py',
'utilities/platform/test',
)]
node = PythonFileTreeNode(py_dir, exclude_dir)
generator.generate_python_package_files(node, rst_dir, 'pmgwidgets')
# 生成主程序的文档
py_dir = os.path.join(base, 'pyminer2') # py源文件的目录
rst_dir = os.path.join(rst_root_dir, 'pyminer') # 根据py生成的rst文件的目录
exclude_dir = [os.path.join(py_dir, p) for p in (
'extensions/packages', # 插件文档独立生成,这里跳过。
# TODO (panhaoyu) 以下内容为无法自动生成的部分,请各位开发者自行检查其中的问题
'ui/pyqtsource_rc.py',
'ui/base/widgets/resources.py',
'core',
'extensions/package_manager/package_manager.py',
'features/io/database.py',
)]
node = PythonFileTreeNode(py_dir, exclude_dir)
generator.generate_python_package_files(node, rst_dir, 'pyminer2')
# 生成插件的文档
py_dir = os.path.join(base, 'pyminer2/extensions/packages') # py源文件的目录
rst_dir = os.path.join(rst_root_dir, 'pkgs') # 根据py生成的rst文件的目录
exclude_dir = [os.path.join(py_dir, p) for p in (
'ipython_console/initialize.py', # IPython控制台的全局文件没什么生成文档的意义跳过
# TODO (panhaoyu) 以下内容为无法自动生成的部分,请各位开发者自行检查其中的问题
'pm_preprocess/ui/pyqtsource_rc.py',
'code_editor',
'data_miner',
'cftool',
'extension_app_demo',
'pm_preprocess',
)]
node = PythonFileTreeNode(py_dir, exclude_dir)
generator.generate_python_package_files(node, rst_dir, 'packages')
# 生成开发工具文档
py_dir = os.path.join(base, 'pyminer_devutils') # py源文件的目录
rst_dir = os.path.join(rst_root_dir, 'dev') # 根据py生成的rst文件的目录
exclude_dir = [os.path.join(py_dir, p) for p in tuple()]
node = PythonFileTreeNode(py_dir, exclude_dir)
generator.generate_python_package_files(node, rst_dir, 'pyminer_devutils')
print('RST文件结构已生成。')
app = Sphinx(
srcdir=rst_root_dir,
confdir=rst_root_dir,
outdir=path.join(path.dirname(__file__), 'build/html'),
doctreedir=path.join(path.dirname(__file__), 'build/doctrees'),
buildername='html',
confoverrides=dict(),
status=sys.stdout,
warning=sys.stderr,
freshenv=False,
warningiserror=False,
tags=[],
verbosity=0,
parallel=0,
# parallel=multiprocessing.cpu_count(),
keep_going=True)
app.build()
# 在上传时,强制重新生成*.rst文件否则可以根据缓存加速rst文件生成过程
if upload:
cmd(f'sphinx-apidoc -fMeo "{alg_rst_dir}" "{alg_src_dir}" -t {template_dir}')
else:
cmd(f'sphinx-apidoc -Meo "{alg_rst_dir}" "{alg_src_dir}" -t {template_dir}')
# 在上传时删除已有的html页面并重新生成否则可以利用缓存加速html生成过程
if upload:
cmd(f'"{make_path}" clean')
cmd(f'"{make_path}" html')
# 进行单元测试
cmd(f'"{make_path}" doctest')
# 在上传时将生成的html文件推送至gitee的pages仓库
if upload:
cmd(f'ghp-import -npfr git@gitee.com:py2cn/pyminer -b pages "{html_path}"')
if __name__ == '__main__':
main()

View File

@ -0,0 +1 @@
关于样式采用了SASS预处理器以使用一系列的高级功能。

View File

@ -0,0 +1,13 @@
@import url("../pydoctheme.css");
img {
border-radius: 2px;
border: solid 2px #dddddd;
margin-top: 10px;
margin-bottom: 10px;
}
.important {
background: #FFC107;
}
/*# sourceMappingURL=pyminer.css.map */

View File

@ -0,0 +1 @@
{"version":3,"sourceRoot":"","sources":["pyminer.sass"],"names":[],"mappings":"AACQ;AAER;EACE;EACA;EACA;EACA;;;AAEF;EACE","file":"pyminer.css"}

View File

@ -0,0 +1,11 @@
//noinspection CssUnknownTarget
@import url('../pydoctheme.css')
img
border-radius: 2px
border: solid 2px #dddddd
margin-top: 10px
margin-bottom: 10px
.important
background: #FFC107

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,22 @@
/*1606211680,,JIT Construction: v1003023083,en_US*/
/**
* Copyright (c) 2017-present, Facebook, Inc. All rights reserved.
*
* You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
* copy, modify, and distribute this software in source code or binary form for use
* in connection with the web services and APIs provided by Facebook.
*
* As with any software that integrates with the Facebook platform, your use of
* this software is subject to the Facebook Platform Policy
* [http://developers.facebook.com/policy/]. This copyright notice shall be
* included in all copies or substantial portions of the software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
(function _(a,b,c,d,e){var f=window.console;f&&Math.floor(new Date().getTime()/1e3)-b>7*24*60*60&&f.warn("The Facebook JSSDK is more than 7 days old.");if(window[c])return;if(!window.JSON)return;var g=window[c]={__buffer:{replay:function(){var a=this,b=function(d){var b=window[c];a.calls[d][0].split(".").forEach(function(a){return b=b[a]});b.apply(null,a.calls[d][1])};for(var d=0;d<this.calls.length;d++)b(d);this.calls=[]},calls:[],opts:null},getUserID:function(){return""},getAuthResponse:function(){return null},getAccessToken:function(){return null},init:function(a){g.__buffer.opts=a}};for(var b=0;b<d.length;b++){f=d[b];if(f in g)continue;var h=f.split("."),i=h.pop(),j=g;for(var k=0;k<h.length;k++)j=j[h[k]]||(j[h[k]]={});j[i]=function(a){if(a==="init")return;return function(){g.__buffer.calls.push([a,Array.prototype.slice.call(arguments)])}}(f)}k=a;h=/Chrome\/(\d+)/.exec(navigator.userAgent);h&&Number(h[1])>=55&&"assign"in Object&&"findIndex"in[]&&(k+="&ua=modern_es6");j=document.createElement("script");j.src=k;j.async=!0;e&&(j.crossOrigin="anonymous");i=document.getElementsByTagName("script")[0];i.parentNode&&i.parentNode.insertBefore(j,i)})("https:\/\/connect.facebook.net\/en_US\/all.js?hash=1f66f14b00fabb1f6f091c9348f79c88", 1606211680, "FB", ["AppEvents.EventNames","AppEvents.ParameterNames","AppEvents.activateApp","AppEvents.clearAppVersion","AppEvents.clearUserID","AppEvents.getAppVersion","AppEvents.getUserID","AppEvents.logEvent","AppEvents.logPageView","AppEvents.logPurchase","AppEvents.setAppVersion","AppEvents.setUserID","AppEvents.updateUserProperties","Canvas.Plugin.showPluginElement","Canvas.Plugin.hidePluginElement","Canvas.Prefetcher.addStaticResource","Canvas.Prefetcher.setCollectionMode","Canvas.getPageInfo","Canvas.scrollTo","Canvas.setAutoGrow","Canvas.setDoneLoading","Canvas.setSize","Canvas.setUrlHandler","Canvas.startTimer","Canvas.stopTimer","Event.subscribe","Event.unsubscribe","XFBML.parse","addFriend","api","getAccessToken","getAuthResponse","getLoginStatus","getUserID","init","login","logout","publish","share","ui"], true);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,376 @@
/*! normalize.css v2.0.1 | MIT License | git.io/normalize */
/* ==========================================================================
HTML5 display definitions
========================================================================== */
/*
* Corrects `block` display not defined in IE 8/9.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
nav,
section,
summary {
display: block;
}
/*
* Corrects `inline-block` display not defined in IE 8/9.
*/
audio,
canvas,
video {
display: inline-block;
}
/*
* Prevents modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/*
* Addresses styling for `hidden` attribute not present in IE 8/9.
*/
[hidden] {
display: none;
}
/* ==========================================================================
Base
========================================================================== */
/*
* 1. Sets default font family to sans-serif.
* 2. Prevents iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: sans-serif; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
-ms-text-size-adjust: 100%; /* 2 */
}
/*
* Removes default margin.
*/
body {
margin: 0;
}
/* ==========================================================================
Links
========================================================================== */
/*
* Addresses `outline` inconsistency between Chrome and other browsers.
*/
a:focus {
outline: thin dotted;
}
/*
* Improves readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* ==========================================================================
Typography
========================================================================== */
/*
* Addresses `h1` font sizes within `section` and `article` in Firefox 4+,
* Safari 5, and Chrome.
*/
h1 {
font-size: 2em;
}
/*
* Addresses styling not present in IE 8/9, Safari 5, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/*
* Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/*
* Addresses styling not present in Safari 5 and Chrome.
*/
dfn {
font-style: italic;
}
/*
* Addresses styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/*
* Corrects font family set oddly in Safari 5 and Chrome.
*/
code,
kbd,
pre,
samp {
font-family: monospace, serif;
font-size: 1em;
}
/*
* Improves readability of pre-formatted text in all browsers.
*/
pre {
white-space: pre;
white-space: pre-wrap;
word-wrap: break-word;
}
/*
* Sets consistent quote types.
*/
q {
quotes: "\201C" "\201D" "\2018" "\2019";
}
/*
* Addresses inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/*
* Prevents `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* ==========================================================================
Embedded content
========================================================================== */
/*
* Removes border when inside `a` element in IE 8/9.
*/
img {
border: 0;
}
/*
* Corrects overflow displayed oddly in IE 9.
*/
svg:not(:root) {
overflow: hidden;
}
/* ==========================================================================
Figures
========================================================================== */
/*
* Addresses margin not present in IE 8/9 and Safari 5.
*/
figure {
margin: 0;
}
/* ==========================================================================
Forms
========================================================================== */
/*
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/*
* 1. Corrects color not being inherited in IE 8/9.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/*
* 1. Corrects font family not being inherited in all browsers.
* 2. Corrects font size not being inherited in all browsers.
* 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome
*/
button,
input,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 2 */
margin: 0; /* 3 */
}
/*
* Addresses Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
button,
input {
line-height: normal;
}
/*
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Corrects inability to style clickable `input` types in iOS.
* 3. Improves usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/*
* Re-set default cursor for disabled elements.
*/
button[disabled],
input[disabled] {
cursor: default;
}
/*
* 1. Addresses box sizing set to `content-box` in IE 8/9.
* 2. Removes excess padding in IE 8/9.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/*
* 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome.
* 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/*
* Removes inner padding and search cancel button in Safari 5 and Chrome
* on OS X.
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/*
* Removes inner padding and border in Firefox 4+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/*
* 1. Removes default vertical scrollbar in IE 8/9.
* 2. Improves readability and alignment in all browsers.
*/
textarea {
overflow: auto; /* 1 */
vertical-align: top; /* 2 */
}
/* ==========================================================================
Tables
========================================================================== */
/*
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1603718180952" class="icon" viewBox="0 0 1260 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4756" xmlns:xlink="http://www.w3.org/1999/xlink" width="246.09375" height="200"><defs><style type="text/css"></style></defs><path d="M1071.261538 992.492308H189.046154V622.276923c0-244.184615 196.923077-441.107692 441.107692-441.107692s441.107692 196.923077 441.107692 441.107692v370.215385z m-803.446153-78.769231h724.676923V622.276923c0-196.923077-165.415385-362.338462-362.338462-362.338461S267.815385 425.353846 267.815385 622.276923v291.446154z" p-id="4757" fill="#1296db"></path><path d="M193.929846 132.726154l65.614769-43.716923L408.024615 311.926154l-65.614769 43.638154z" p-id="4758" fill="#1296db"></path><path d="M220.553846 110.276923m-78.769231 0a78.769231 78.769231 0 1 0 157.538462 0 78.769231 78.769231 0 1 0-157.538462 0Z" p-id="4759" fill="#1296db"></path><path d="M855.118769 305.467077l148.558769-222.838154 65.536 43.716923-148.558769 222.916923z" p-id="4760" fill="#1296db"></path><path d="M1039.753846 110.276923m-78.769231 0a78.769231 78.769231 0 1 0 157.538462 0 78.769231 78.769231 0 1 0-157.538462 0Z" p-id="4761" fill="#1296db"></path><path d="M228.430769 882.215385H157.538462c-63.015385 0-118.153846-55.138462-118.153847-118.153847V630.153846c0-63.015385 55.138462-118.153846 118.153847-118.153846h70.892307v78.769231H157.538462c-23.630769 0-39.384615 15.753846-39.384616 39.384615v133.907692c0 23.630769 15.753846 39.384615 39.384616 39.384616h70.892307v78.769231zM1102.769231 882.215385h-70.892308v-78.769231h70.892308c23.630769 0 39.384615-15.753846 39.384615-39.384616V630.153846c0-23.630769-15.753846-39.384615-39.384615-39.384615h-70.892308v-78.769231h70.892308c63.015385 0 118.153846 55.138462 118.153846 118.153846v133.907692c0 63.015385-55.138462 118.153846-118.153846 118.153847z" p-id="4762" fill="#1296db"></path><path d="M448.984615 622.276923m-78.76923 0a78.769231 78.769231 0 1 0 157.538461 0 78.769231 78.769231 0 1 0-157.538461 0Z" p-id="4763" fill="#1296db"></path><path d="M811.323077 622.276923m-78.769231 0a78.769231 78.769231 0 1 0 157.538462 0 78.769231 78.769231 0 1 0-157.538462 0Z" p-id="4764" fill="#1296db"></path></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,289 @@
<!DOCTYPE html>
<!-- saved from url=(0029)http://matopen.com/dashboard/ -->
<html lang="en" style="" class=" js flexbox flexboxlegacy canvas canvastext webgl no-touch geolocation postmessage websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients cssreflections csstransforms csstransforms3d csstransitions fontface no-generatedcontent video audio localstorage sessionstorage webworkers no-applicationcache svg inlinesvg smil svgclippaths"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<!-- Always force latest IE rendering engine or request Chrome Frame -->
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Use title if it's in the page YAML frontmatter -->
<title>Welcome to PyMiner</title>
<meta name="description" content="XAMPP is an easy to install Apache distribution containing MariaDB, PHP and Perl.">
<meta name="keywords" content="xampp, apache, php, perl, mariadb, open source distribution">
<link href="_static/index_files/normalize.css" rel="stylesheet" type="text/css"><link href="_static/index_files/all.css" rel="stylesheet" type="text/css">
<link href="_static/index_files/font-awesome.min.css" rel="stylesheet" type="text/css">
<script src="_static/index_files/all.js.download" async="" crossorigin="anonymous"></script><script id="facebook-jssdk" src="_static/index_files/all.js(1).download"></script><script src="_static/index_files/modernizr.js.download" type="text/javascript"></script>
<link href="http://matopen.com/dashboard/images/favicon.png" rel="icon" type="image/png">
<style type="text/css" data-fbcssmodules="css:fb.css.basecss:fb.css.dialog css:fb.css.iframewidget">.fb_hidden{position:absolute;top:-10000px;z-index:10001}.fb_reposition{overflow:hidden;position:relative}.fb_invisible{display:none}.fb_reset{background:none;border:0;border-spacing:0;color:#000;cursor:auto;direction:ltr;font-family:"lucida grande", tahoma, verdana, arial, sans-serif;font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:1;margin:0;overflow:visible;padding:0;text-align:left;text-decoration:none;text-indent:0;text-shadow:none;text-transform:none;visibility:visible;white-space:normal;word-spacing:normal}.fb_reset>div{overflow:hidden}@keyframes fb_transform{from{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.fb_animate{animation:fb_transform .3s forwards}
.fb_dialog{background:rgba(82, 82, 82, .7);position:absolute;top:-10000px;z-index:10001}.fb_dialog_advanced{border-radius:8px;padding:10px}.fb_dialog_content{background:#fff;color:#373737}.fb_dialog_close_icon{background:url(https://static.xx.fbcdn.net/rsrc.php/v3/yq/r/IE9JII6Z1Ys.png) no-repeat scroll 0 0 transparent;cursor:pointer;display:block;height:15px;position:absolute;right:18px;top:17px;width:15px}.fb_dialog_mobile .fb_dialog_close_icon{left:5px;right:auto;top:5px}.fb_dialog_padding{background-color:transparent;position:absolute;width:1px;z-index:-1}.fb_dialog_close_icon:hover{background:url(https://static.xx.fbcdn.net/rsrc.php/v3/yq/r/IE9JII6Z1Ys.png) no-repeat scroll 0 -15px transparent}.fb_dialog_close_icon:active{background:url(https://static.xx.fbcdn.net/rsrc.php/v3/yq/r/IE9JII6Z1Ys.png) no-repeat scroll 0 -30px transparent}.fb_dialog_iframe{line-height:0}.fb_dialog_content .dialog_title{background:#6d84b4;border:1px solid #365899;color:#fff;font-size:14px;font-weight:bold;margin:0}.fb_dialog_content .dialog_title>span{background:url(https://static.xx.fbcdn.net/rsrc.php/v3/yd/r/Cou7n-nqK52.gif) no-repeat 5px 50%;float:left;padding:5px 0 7px 26px}body.fb_hidden{height:100%;left:0;margin:0;overflow:visible;position:absolute;top:-10000px;transform:none;width:100%}.fb_dialog.fb_dialog_mobile.loading{background:url(https://static.xx.fbcdn.net/rsrc.php/v3/ya/r/3rhSv5V8j3o.gif) white no-repeat 50% 50%;min-height:100%;min-width:100%;overflow:hidden;position:absolute;top:0;z-index:10001}.fb_dialog.fb_dialog_mobile.loading.centered{background:none;height:auto;min-height:initial;min-width:initial;width:auto}.fb_dialog.fb_dialog_mobile.loading.centered #fb_dialog_loader_spinner{width:100%}.fb_dialog.fb_dialog_mobile.loading.centered .fb_dialog_content{background:none}.loading.centered #fb_dialog_loader_close{clear:both;color:#fff;display:block;font-size:18px;padding-top:20px}#fb-root #fb_dialog_ipad_overlay{background:rgba(0, 0, 0, .4);bottom:0;left:0;min-height:100%;position:absolute;right:0;top:0;width:100%;z-index:10000}#fb-root #fb_dialog_ipad_overlay.hidden{display:none}.fb_dialog.fb_dialog_mobile.loading iframe{visibility:hidden}.fb_dialog_mobile .fb_dialog_iframe{position:sticky;top:0}.fb_dialog_content .dialog_header{background:linear-gradient(from(#738aba), to(#2c4987));border-bottom:1px solid;border-color:#043b87;box-shadow:white 0 1px 1px -1px inset;color:#fff;font:bold 14px Helvetica, sans-serif;text-overflow:ellipsis;text-shadow:rgba(0, 30, 84, .296875) 0 -1px 0;vertical-align:middle;white-space:nowrap}.fb_dialog_content .dialog_header table{height:43px;width:100%}.fb_dialog_content .dialog_header td.header_left{font-size:12px;padding-left:5px;vertical-align:middle;width:60px}.fb_dialog_content .dialog_header td.header_right{font-size:12px;padding-right:5px;vertical-align:middle;width:60px}.fb_dialog_content .touchable_button{background:linear-gradient(from(#4267B2), to(#2a4887));background-clip:padding-box;border:1px solid #29487d;border-radius:3px;display:inline-block;line-height:18px;margin-top:3px;max-width:85px;padding:4px 12px;position:relative}.fb_dialog_content .dialog_header .touchable_button input{background:none;border:none;color:#fff;font:bold 12px Helvetica, sans-serif;margin:2px -12px;padding:2px 6px 3px 6px;text-shadow:rgba(0, 30, 84, .296875) 0 -1px 0}.fb_dialog_content .dialog_header .header_center{color:#fff;font-size:16px;font-weight:bold;line-height:18px;text-align:center;vertical-align:middle}.fb_dialog_content .dialog_content{background:url(https://static.xx.fbcdn.net/rsrc.php/v3/y9/r/jKEcVPZFk-2.gif) no-repeat 50% 50%;border:1px solid #4a4a4a;border-bottom:0;border-top:0;height:150px}.fb_dialog_content .dialog_footer{background:#f5f6f7;border:1px solid #4a4a4a;border-top-color:#ccc;height:40px}#fb_dialog_loader_close{float:left}.fb_dialog.fb_dialog_mobile .fb_dialog_close_icon{visibility:hidden}#fb_dialog_loader_spinner{animation:rotateSpinner 1.2s linear infinite;background-color:transparent;background-image:url(https://static.xx.fbcdn.net/rsrc.php/v3/yD/r/t-wz8gw1xG1.png);background-position:50% 50%;background-repeat:no-repeat;height:24px;width:24px}@keyframes rotateSpinner{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}
.fb_iframe_widget{display:inline-block;position:relative}.fb_iframe_widget span{display:inline-block;position:relative;text-align:justify}.fb_iframe_widget iframe{position:absolute}.fb_iframe_widget_fluid_desktop,.fb_iframe_widget_fluid_desktop span,.fb_iframe_widget_fluid_desktop iframe{max-width:100%}.fb_iframe_widget_fluid_desktop iframe{min-width:220px;position:relative}.fb_iframe_widget_lift{z-index:1}.fb_iframe_widget_fluid{display:inline}.fb_iframe_widget_fluid span{width:100%}</style><style type="text/css">
@font-face {
font-family: "element-icons";
src: url('chrome-extension://moombeodfomdpjnpocobemoiaemednkg/fonts/element-icons.woff') format('woff'),
url('chrome-extension://moombeodfomdpjnpocobemoiaemednkg/fonts/element-icons.ttf ') format('truetype'); /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
}
</style></head>
<body class="index" cz-shortcut-listen="true">
<div id="fb-root" class=" fb_reset"><div style="position: absolute; top: -10000px; width: 0px; height: 0px;"><div></div></div></div>
<script>(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/all.js#xfbml=1&appId=277385395761685";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>
<div class="contain-to-grid">
<nav class="top-bar" data-topbar="">
<ul class="title-area">
<li class="name">
<h1><a href="http://matopen.com/dashboard/index.html">PyMiner</a></h1>
</li>
<li class="toggle-topbar menu-icon">
<a href="http://matopen.com/dashboard/#">
<span>Menu</span>
</a>
</li>
</ul>
<section class="top-bar-section">
<!-- Right Nav Section -->
<ul class="right">
<li class=""><a target="_blank" href="https://gitee.com/py2cn/pyminer/wikis/%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B?sort_id=3137860">快速入门</a></li>
<li class=""><a target="_blank" href="http://124.70.76.32:3000/PyMiner_dev_kit.zip">下载</a></li>
<li class=""><a href="contents.html">开发者文档</a></li>
<li class=""><a target="_blank" href="https://gitee.com/py2cn/pyminer/board">当前进度</a></li>
<li class=""><a target="_blank" href="https://gitee.com/py2cn/pyminer/issues">问题汇总</a></li>
<li class=""><a target="_blank" href="https://gitee.com/py2cn/pyminer/wikis/%E9%A1%B9%E7%9B%AE%E8%AF%B4%E6%98%8E?sort_id=2796707">项目说明</a></li>
<li class=""><a target="_blank" href="https://gitee.com/py2cn/pyminer/wikis/%E5%A6%82%E4%BD%95%E5%8F%82%E4%B8%8E%E9%A1%B9%E7%9B%AE%EF%BC%9F?sort_id=2820268">如何参与</a></li>
<li class=""><a target="_blank" href="https://gitee.com/py2cn/pyminer/wikis/%E6%8D%90%E8%B5%A0?sort_id=2925146">捐赠</a></li>
</ul>
</section>
</nav>
</div>
<div id="wrapper">
<div class="hero">
<div class="row">
<div class="large-12 columns">
<h1><img src="_static/index_files/robot.svg"> <span>开源的,跨平台数据处理解决方案。</span></h1>
</div>
</div>
</div>
<div class="row">
<div class="large-12 columns">
<h2>Welcome to PyMiner</h2>
</div>
</div>
<div class="row">
<div class="large-12 columns">
<p style="font-size: 20px;">PyMiner一款基于数据空间的数学工具通过加载各种插件实现不同的需求用易于操作的形式在统一的界面中通过数据计算实现数据科学家所设想的任务。</p>
<p style="font-size: 20px;color: #008CBA;">坚持对标国际一流,实现面向未来的设计,以自主创新为战略基石,积极吸收凝聚国内外各种优势资源和创新要素,致力于突破掌握一批数据算法模型以及仿真领域研制关键技术!</p>
<br>
<div class="alert-box warning"><p style="font-size: 24px;">我们正在基于Python研究开源的 MATLAB目前项目刚刚起步需要更多热爱开源的Python、MATLAB等各路大牛加入有兴趣请加入QQ群945391275欢迎大佬加入</p><p></p></div>
<br>
<br>
<ul id="downloads">
<li id="download-windows">
<h3><img src="_static/index_files/robot.svg">PyMiner <strong>源码仓库</strong> </h3>
<table>
<tbody><tr>
<th width="25%">仓库名</th>
<th width="25%" class="show-for-medium-up">版本号</th>
<th width="25%" class="show-for-medium-up">源码地址</th>
<th width="25%" class="show-for-medium-up">下载地址</th>
</tr>
<tr>
<td rowspan="1">Gitee</td>
<td class="show-for-medium-up">PyMiner 1.0.1</td>
<td class="show-for-medium-up">
<a class="button" data-delayed-href="/download_success.html" target="_blank" href="https://gitee.com/py2cn/pyminer">开源仓库 →</a>
</td>
<td class="show-for-medium-up">
<a class="button" data-delayed-href="/download_success.html" target="_blank" href="http://124.70.76.32:3000/PyMiner_dev_kit.zip">立即下载 →</a>
</td>
</tr>
<tr>
<td rowspan="1">Github</td>
<td class="show-for-medium-up">PyMiner 1.0.1</td>
<td class="show-for-medium-up">
<a class="button" data-delayed-href="/download_success.html" target="_blank" href="https://github.com/aboutlong/pyminer">开源仓库 →</a>
</td>
<td class="show-for-medium-up">
<a class="button" data-delayed-href="/download_success.html" target="_blank" href="https://gitee.com/py2cn/pyminer">立即下载 →</a>
</td>
</tr>
</tbody></table>
</li>
</ul>
<div class="text-center">
<a class="button icon" id="forum" target="_blank" href="https://www.kuxai.com/"> <img width="50" height="50" src="_static/index_files/robot.svg">Python机器学习社区
<span>Aihub 一个朝气蓬勃的技术社区.</span>
</a> </div>
<p>
</p><h3><strong>技术说明</strong></h3>
开发环境基于Python3.8+PyQt5.15建议安装Pycharm创建python虚拟环境进行开发。
<p></p>
</div>
</div>
<div class="row">
<div class="large-12 columns">
<h3><strong>功能介绍</strong></h3>
</div>
</div>
<div class="row">
<div class="large-8 columns">
<ul>
<li>强大的代码编辑器让你误以为是专业的IDE代码着色、智能提示应有尽有<br><img width="500" src="_static/index_files/code.png"></li>
<li>开放的控制台ipython集成它能实现的我也可以</li>
<li>强大的绘图功能,专业出版水准图片,增强型参数调整</li>
<li>应用商店,开放的插件式开发,自定义专业领域的应用</li>
<li>自建函数库、算法库,保持开放,面向未来!</li>
</ul>
</div>
<div class="large-4 columns">
<aside>
<h4>特别感谢(捐赠人)</h4>
<ul>
<li> QuietPear</li>
<li> 州的先生</li>
<li> 邱峰</li>
<li> 一个小白</li>
</ul>
<h4>特别感谢(开发者)</h4>
<ul>
<li> 侯展意</li>
<li> py2cn</li>
<li> Junruoyu-Zheng</li>
<li> nihk</li>
<li> 心随风</li>
<li> Irony</li>
<li> 冰中火</li>
<li> houxinluo</li>
<li> cl-jiang</li>
<li> 开始说故事</li>
</ul>
</aside>
</div>
</div>
<div class="row">
<div class="large-12 columns">
<h3><strong>安装体验</strong></h3>
</div>
</div>
<div class="row">
<div class="large-12 columns">
<p>
#第一步:下载源代码<br>
git clone https://gitee.com/py2cn/pyminer.git <br>
#第二步同步安装依赖包和PyMiner如果遇到安装失败的情况需要手动安装<br>
python -m pip install -i https://mirrors.cloud.tencent.com/pypi/simple -r requirements.txt <br>
#第三步:运行主程序<br>
python app2.py<br>
</p>
</div>
</div>
<div class="row">
<div class="large-12 columns">
<h3><strong>为什么要做这个?目的是什么?</strong></h3>
</div>
</div>
<div class="row">
<div class="large-12 columns">
<p>
2020年6月6日开始哈工大等中国高校被禁用MATLAB。在此之前我们发现在工业软件、行业软件除了MATLAB以外还有SAS、SPSS、FICO、Adobe等都是我们高度依赖且无法替代的行业软件。 为此这个项目的根本目标就是实现MATLAB替代短期内至少是实现在某一方面的替代并逐步实现超越MATLAB并以抛砖引玉的形式让更多人加入到国产替代的行列中PyMiner坚持对标国际一流实现面向未来的设计以自主创新为战略基石积极吸收凝聚国内外各种优势资源和创新要素致力于突破掌握一批数据算法模型以及仿真领域研制关键技术
</p>
</div>
</div>
<div class="row">
<div class="large-12 columns">
<h3><strong>预览截图</strong></h3>
</div>
</div>
<div class="row">
<div class="large-12 columns">
<div style="text-align: center; margin-bottom: 1em;">
<p style="text-align: left;">主界面截图</p>
<img width="1024" data-2x="https://gitee.com/py2cn/pyminer/raw/master/pyminer2/ui/source/screenshot/main.png" src="_static/index_files/main.png">
</div>
<div style="text-align: center; margin-bottom: 1em;">
<p style="text-align: left;">绘图截图</p>
<img width="1024" data-2x="https://gitee.com/py2cn/pyminer/raw/master/pyminer2/ui/source/screenshot/check_data.png" src="_static/index_files/check_data.png">
</div>
<div style="text-align: center; margin-bottom: 1em;">
<p style="text-align: left;">代码提示截图</p>
<img width="1024" data-2x="https://gitee.com/py2cn/pyminer/raw/master/pyminer2/ui/source/screenshot/code.png" src="_static/index_files/code.png">
</div>
</div>
</div>
</div>
<footer>
<div class="row">
<div class="large-12 columns">
<div class="row">
<ul>
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="http://mail.qq.com/cgi-bin/qm_share?t=qm_mailme&amp;email=sMTV0d3wwMmC096e09-d" style="text-decoration:none;"><img src="_static/index_files/ico_mailme_01.png"></a>
</li>
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="https://mirrors.tuna.tsinghua.edu.cn/">清华大学开源软件镜像站</a></li>
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="https://www.python.org/">Python官网</a></li>
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="https://www.jetbrains.com/pycharm/download/">Pycharm</a></li>
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="https://www.anaconda.com/">Anaconda</a></li>
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="https://pandas.pydata.org/">Pandas</a></li>
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="https://numpy.org/">Numpy</a></li>
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="https://matplotlib.org/">matplotlib</a></li>
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="https://scipy.org/">scipy</a></li>
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="https://scikit-learn.org/stable/">scikit-learn</a></li>
<li style="float: left;display: block;padding:8px 10px;"><a target="_blank" href="https://seaborn.pydata.org/">seaborn</a></li>
</ul>
</div>
<div class="row">
<p class="text-center">Copyright (c) 2020, PyMiner Development Team<br>
<a target="_blank" href="https://beian.miit.gov.cn/">粤ICP备2020105986号</a></p>
</div>
</div>
</div>
</footer>
<!-- JS Libraries -->
<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
<script src="_static/index_files/all.js(2).download" type="text/javascript"></script>
</body><div style="all: initial;"><div></div></div></html>

View File

@ -10,12 +10,17 @@
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
from os import path
from recommonmark.transform import AutoStructify
from recommonmark.parser import CommonMarkParser
from sphinx.application import Sphinx
base_path = path.abspath(path.join(__file__, '../../..'))
sys.path.insert(0, base_path)
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(os.path.dirname(__file__)))))
# -- Project information -----------------------------------------------------
project = 'pyminer'
copyright = '2020, py2cn'
author = 'py2cn'
@ -32,8 +37,10 @@ extensions = [
'sphinx.ext.intersphinx',
'sphinx.ext.todo',
'sphinx.ext.coverage',
'sphinx.ext.viewcode',
'sphinx.ext.mathjax',
'numpydoc',
'recommonmark',
# 'numpydoc',
]
# Add any paths that contain templates here, relative to this directory.
@ -44,7 +51,7 @@ templates_path = ['_templates']
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = 'zh'
language = 'zh_CN'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
@ -56,11 +63,47 @@ exclude_patterns = []
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
html_theme = "pydata_sphinx_theme"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# 将首页采用独立的模板进行渲染,即配置独立的首页,同时将 rst 自动生成的首页,由 index 调整为 contents 以免冲突
html_additional_pages = {'index': 'index.html'}
master_doc = 'contents'
add_module_names = False
# 配置TODO插件
todo_include_todos = True
# 借助recommonmark插件同时支持 *.md 和 *.rst 文件进行文档的撰写。
source_suffix = {
'.rst': 'restructuredtext',
'.md': 'markdown',
}
# 使得类的文档字符串和 ``__init__`` 方法的文档字符串都显出来
autoclass_content = 'both'
# 使得生成的文档的顺序与源代码一致
autodoc_member_order = 'bysource'
# 配置模板的主题样式
html_style = 'css/pyminer.css'
# 显示 .. sectionauthor 指令指定的作者
show_authors = True
source_parsers = {
'.md': CommonMarkParser,
}
def setup(app: Sphinx):
app.add_config_value('recommonmark_config', {
'auto_toc_tree_section': 'Contents',
}, True)
app.add_transform(AutoStructify)

20
docs/source/contents.rst Normal file
View File

@ -0,0 +1,20 @@
===================================
PyMiner帮助文档
===================================
.. toctree::
:maxdepth: 2
主程序部分 <pyminer/index.rst>
界面部分 <pmg/index.rst>
算法部分 <alg/index.rst>
插件部分 <pkgs/index.rst>
开发工具部分 <dev/index.rst>
参与贡献 <contribute/index.rst>
模块索引
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@ -0,0 +1,12 @@
## 参与贡献
目前可以看我们的 [WIKI页面](https://gitee.com/py2cn/pyminer/wikis/%E4%B8%BB%E9%A1%B5?sort_id=2761029)。
参与贡献的第一步就是将WIKI上面的“参与贡献”转移到这里:P。
文档目前采用 Google 风格的文档字符串进行生成,所有内容都写在文档字符串里。
包的文档字符串、模块的文档字符串、函数与方法的文档字符串,都可以被检索。
各位开发者可以根据自己的需要在不同的位置添加文档字符串。
如果需要重新生成文档,可以删去`alg` 、`pkgs`、`pmg`、`pyminer`、`build`五个文件夹,
然后运行`make_doc`即可重新生成。

View File

@ -1,20 +0,0 @@
.. pyminer documentation master file, created by
.. sphinx-quickstart on Thu Oct 22 14:30:12 2020.
.. You can adapt this file completely to your liking, but it should at least
.. contain the root `toctree` directive.
Welcome to pyminer's documentation!
===================================
.. toctree::
:maxdepth: 2
:caption: Readme
module <alg/modules.rst>
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@ -1,6 +0,0 @@
{% set names = basename.split('.') %}
{{ names[-1] | heading }}
.. automodule:: {{ qualname }}
:members:

View File

@ -1,51 +0,0 @@
{%- macro automodule(modname, options) -%}
.. automodule:: {{ modname }}
:members: __doc__
{%- endmacro %}
{%- macro toctree(docnames) -%}
.. toctree::
:maxdepth: {{ maxdepth }}
{% for docname in docnames %}
{{ docname }}
{%- endfor %}
{%- endmacro %}
{%- if is_namespace %}
{{- [pkgname, "namespace"] | join(" ") | e | heading }}
{% else %}
{{- [pkgname, "package"] | join(" ") | e | heading }}
{% endif %}
{%- if modulefirst and not is_namespace %}
{{ automodule(pkgname, automodule_options) }}
{% endif %}
{%- if subpackages %}
Subpackages
-----------
{{ toctree(subpackages) }}
{% endif %}
{%- if submodules %}
Submodules
----------
{% if separatemodules %}
{{ toctree(submodules) }}
{% else %}
{%- for submodule in submodules %}
{% if show_headings %}
{{- [submodule, "module"] | join(" ") | e | heading(2) }}
{% endif %}
{{ automodule(submodule, automodule_options) }}
{% endfor %}
{%- endif %}
{%- endif %}
{%- if not modulefirst and not is_namespace %}
Module contents
---------------
{{ automodule(pkgname, automodule_options) }}
{% endif %}

10
docs/upload.py Normal file
View File

@ -0,0 +1,10 @@
import os
import sys
# 项目的根路径即pyminer项目的根文件夹
base = os.path.dirname(os.path.dirname(__file__))
sys.path.insert(0, base)
html_path = os.path.join(base, 'docs', 'build', 'html')
for line in os.popen(f'ghp-import -npfr git@gitee.com:py2cn/pyminer -b pages "{html_path}"'):
print(line, end='')

42
examples/HereWeGo.py Normal file
View File

@ -0,0 +1,42 @@
# -*- coding:utf-8 -*-
__author__ = 'boredbird'
import os
import numpy as np
import woe.feature_process as fp
import woe.GridSearch as gs
if __name__ == '__main__':
config_path = os.getcwd() + '\\config.csv'
data_path = os.getcwd() + '\\UCI_Credit_Card.csv'
feature_detail_path = os.getcwd() + '\\features_detail.csv'
rst_pkl_path = os.getcwd() + '\\woe_rule.pkl'
# train woe rule
feature_detail, rst = fp.process_train_woe(infile_path=data_path
, outfile_path=feature_detail_path
, rst_path=rst_pkl_path
, config_path=config_path)
# proc woe transformation
woe_train_path = os.getcwd() + '\\dataset_train_woed.csv'
fp.process_woe_trans(data_path, rst_pkl_path, woe_train_path, config_path)
# here i take the same dataset as test dataset
woe_test_path = os.getcwd() + '\\dataset_test_woed.csv'
fp.process_woe_trans(data_path, rst_pkl_path, woe_test_path, config_path)
print('###TRAIN SCORECARD MODEL###')
params = {}
params['dataset_path'] = woe_train_path
params['validation_path'] = woe_test_path
params['config_path'] = config_path
params['df_coef_path'] = os.getcwd() + '\\df_model_coef_path.csv'
params['pic_coefpath'] = os.getcwd() + '\\model_coefpath.png'
params['pic_performance'] = os.getcwd() + '\\model_performance_path.png'
params['pic_coefpath_title'] = 'model_coefpath'
params['pic_performance_title'] = 'model_performance_path'
params['var_list_specfied'] = []
params['cs'] = np.logspace(-4, -1, 40)
for key, value in params.items():
print(key, ': ', value)
gs.grid_search_lr_c_main(params)

43
examples/README.md Normal file
View File

@ -0,0 +1,43 @@
# Dataset Information
This dataset contains information on default payments, demographic factors, credit data, history of payment, and bill statements of credit card clients in Taiwan from April 2005 to September 2005.
**YOU SHOULD SPECIFY THE VARIABLES DTYPES WITH config.csv**
Appointment:
continuous variables: is_tobe_bin=1 and is_candidate=1
discrete variables: is_tobe_bin=0 and is_candidate=1
## Content
There are 25 variables:
- ID: ID of each client
- LIMIT_BAL: Amount of given credit in NT dollars (includes individual and family/supplementary credit
- SEX: Gender (1=male, 2=female)
- EDUCATION: (1=graduate school, 2=university, 3=high school, 4=others, 5=unknown, 6=unknown)
- MARRIAGE: Marital status (1=married, 2=single, 3=others)
- AGE: Age in years
- PAY_0: Repayment status in September, 2005 (-1=pay duly, 1=payment delay for one month, 2=payment delay for two months, ... 8=payment delay for eight months, 9=payment delay for nine months and above)
- PAY_2: Repayment status in August, 2005 (scale same as above)
- PAY_3: Repayment status in July, 2005 (scale same as above)
- PAY_4: Repayment status in June, 2005 (scale same as above)
- PAY_5: Repayment status in May, 2005 (scale same as above)
- PAY_6: Repayment status in April, 2005 (scale same as above)
- BILL_AMT1: Amount of bill statement in September, 2005 (NT dollar)
- BILL_AMT2: Amount of bill statement in August, 2005 (NT dollar)
- BILL_AMT3: Amount of bill statement in July, 2005 (NT dollar)
- BILL_AMT4: Amount of bill statement in June, 2005 (NT dollar)
- BILL_AMT5: Amount of bill statement in May, 2005 (NT dollar)
- BILL_AMT6: Amount of bill statement in April, 2005 (NT dollar)
- PAY_AMT1: Amount of previous payment in September, 2005 (NT dollar)
- PAY_AMT2: Amount of previous payment in August, 2005 (NT dollar)
- PAY_AMT3: Amount of previous payment in July, 2005 (NT dollar)
- PAY_AMT4: Amount of previous payment in June, 2005 (NT dollar)
- PAY_AMT5: Amount of previous payment in May, 2005 (NT dollar)
- PAY_AMT6: Amount of previous payment in April, 2005 (NT dollar)
- default.payment.next.month: Default payment (1=yes, 0=no)

43
examples/README_ZH.md Normal file
View File

@ -0,0 +1,43 @@
# 数据集信息
本数据集包含2005年4月至2005年9月台湾信用卡客户的逾期、个人信息、信用数据、付款历史及账单信息。
**你应该通过 config.csv 指定变量类型**
约定:
连续变量: is_tobe_bin=1 且 is_candidate=1
离散变量: is_tobe_bin=0 且 is_candidate=1
## 内容
本数据集包含25个变量:
- ID: 每个客户的ID
- LIMIT_BAL:授信金额(新台币,含个人及家庭/补充授信)
- SEX: 性别 (1=男, 2=女)
- EDUCATION: (1=研究生, 2=本科, 3=高中, 4=其他, 5=未知, 6=未知)
- MARRIAGE: 婚姻状态 (1=已婚, 2=单身, 3=其他)
- AGE: 年龄
- PAY_0: 2005年9月还款状态 (-1=正常支付, 1=付款延迟1个月, 2=付款延迟2个月, ... 8=付款延迟8个月, 9=付款延迟9个月及以上)
- PAY_2: 2005年8月还款状态 (单位同上)
- PAY_3: 2005年7月还款状态 (单位同上)
- PAY_4: 2005年6月还款状态 (单位同上)
- PAY_5: 2005年5月还款状态 (单位同上)
- PAY_6: 2005年4月还款状态 (单位同上)
- BILL_AMT1: 2005年9月的账单金额 (新台币)
- BILL_AMT2: 2005年8月的账单金额 (新台币)
- BILL_AMT3: 2005年7月的账单金额 (新台币)
- BILL_AMT4: 2005年6月的账单金额 (新台币)
- BILL_AMT5: 2005年5月的账单金额 (新台币)
- BILL_AMT6: 2005年4月的账单金额 (新台币)
- PAY_AMT1: 2005年9月的提前还款金额 (新台币)
- PAY_AMT2: 2005年8月的提前还款金额 (新台币)
- PAY_AMT3: 2005年7月的提前还款金额 (新台币)
- PAY_AMT4: 2005年6月的提前还款金额 (新台币)
- PAY_AMT5: 2005年5月的提前还款金额 (新台币)
- PAY_AMT6: 2005年4月的提前还款金额 (新台币)
- target: 是否逾期 (1=是, 0=否)

View File

Can't render this file because it is too large.

View File

Can't render this file because it is too large.

View File

Can't render this file because it is too large.

BIN
examples/woe_rule.pkl Normal file

Binary file not shown.

7
pip.ini Normal file
View File

@ -0,0 +1,7 @@
[global]
index-url=http://mirrors.aliyun.com/pypi/simple/
[install]
trusted-host=mirrors.aliyun.com

View File

@ -1,254 +0,0 @@
[TOC]
# pmgwidgets常用控件介绍
``` python
from pmgwidgets import {控件名}
```
用以上语句即可导入相应的控件。
控件的有关示例见pmgwidgets/tests文件夹。
## 设置控件SettingsPanel
查看本文中的示例即可。运行示例即可得到以下界面:
![](doc_figures/settings_panel.png)
### 数据结构与格式
创建这个界面只需要一个json式的数据结构如下所示
```python
views = [('line_edit', 'name', 'What\'s your name?', 'hzy'),
('number', 'age', 'How old are you?', 88, 'years old', (0, 150)),
('number', 'height', 'How High could This Plane fly?', 12000, 'm', (10, 20000)),
('bool', 'sport', 'do you like sport', True),
('choose_box', 'plane_type', 'plane type', 'f22', ['f22', 'f18', 'j20', 'su57'],
['f22战斗机', 'f18战斗轰炸机', 'j20战斗机', 'su57战斗机']),
('color', 'color', 'Which color do u like?', (0, 200, 0))]
```
这些数据的格式为:
数据类型;数据名称;提示信息;初始值。第四位之后的其他数据为修饰信息,比如单位、范围等。
| 返回值类型 | 1:选择器名称 | 2:数据名称 | 3提示信息 | 4初始值 | 5 | 6 |
| ---------------------------------------- | ------------ | ---------- | ----------- | -------------------------- | -------- | --------------- |
| 字符串型str | 'line_edit' | str | str | 初始值str | / | / |
| 整型或者浮点int/float字符串型str | 'number' | 名称:str | str | int/float初始值str | 单位str | 范围min,max |
| 布尔型bool | 'bool' | str | str | bool | / | / |
| 任意类型多选一str | 'choose_box' | str | str | object任意类型* | 选项列表 | 选项文本列表 |
| 颜色(返回形如'#a0b89d'的颜色字符串) | 'color' | 'color' | str | tupleRGB,每位为0~255的整数 | / | |
*:注意,任意类型,多选一的下拉列表中,列表可以填入任意类型。但是你所输入的初始值,必须在选项列表中存在,否则会抛出异常。
### 创建一个设置面板:
- 控件列表可以在初始化的时候传入,像这样:
```python
views = [('line_edit', 'name', 'What\'s your name?', 'hzy'),
('number', 'age', 'How old are you?', 88, 'years old', (0, 150))]
sp = SettingsPanel(views=views, layout_dir='v')
```
- 控件列表也可以在初始化完成之后传入:
```python
views = [('line_edit', 'name', 'What\'s your name?', 'hzy'),
('number', 'age', 'How old are you?', 88, 'years old', (0, 150))]
sp = SettingsPanel()
sp.set_items(views)
```
写法与初始化时相同。
- `set_items`可以多次调用。如果调用的时候设置面板不为空,那么之前的设置项都会被清空。
### 获取其上的控件
调用`get_ctrl(ctrl_name:str)`方法,可以获取其上的控件。
如:
```python
views = [('line_edit', 'name', 'What\'s your name?', 'hzy'),
('number', 'age', 'How old are you?', 88, 'years old', (0, 150))]
sp = SettingsPanel(views=views, layout_dir='v')
name_widget = sp.get_ctrl('name')
```
- 注意如果控件名称不存在则返回None。注意做好类型判断。
### 获取值
#### 获取单个控件值
获取控件之后调用控件的set_value方法。
```python
views = [('line_edit', 'name', 'What\'s your name?', 'hzy'),
('number', 'age', 'How old are you?', 88, 'years old', (0, 150))]
sp = SettingsPanel(views=views, layout_dir='v')
name_widget = sp.get_ctrl('name')
name_widget.get_value()
```
#### 获取整体值
调用SettingsPanel的get_value()方法,可以返回一个字典。键就是以上表格的“数据名称”列,返回的是相应的值。
如果未做任何改动,那么返回的值就是初始值,也就是表格“初始值”列的内容。
比如之前所述的views列表输入的初始值最终得出的结果是这样的dict
```python
values = sp.get_value()
values = {'name': 'hzy', 'age': 88.0, 'height': 12000.0, 'sport': True, 'plane_type': 'f22', 'color': (0, 200, 0)}
```
### 设置值
比如之前所述的views列表输入的初始值最终得出的结果是这样的dict
```python
views = [('line_edit', 'name', 'What\'s your name?', 'James'),
('number', 'age', 'How old are you?', 88, 'years old', (0, 150))]
sp = SettingsPanel(views=views, layout_dir='v')
name_widget = sp.get_ctrl('name')
name_widget.set_value("John")
```
set_value方法传入的参数类型必须与初始值相同。
### 设置控件的参数
调用`set_params(*params)`方法。
params指的是从表格的初始值列之后的几项。比如选择菜单的选项、数据型输入框的范围。
控件的参数指的就是从第5项开始含第5项以后的内容。比如对于一个数值型控件
如:
```python
views = [('line_edit', 'name', 'What\'s your name?', 'hzy'),
('number', 'age', 'How old are you?', 88, 'years old', (0, 150))]
sp = SettingsPanel(views=views, layout_dir='v')
num_widget = sp.get_ctrl('name')
if num_widget is not None:
num_widget.set_params('years old',(10,145))
```
以上示例就是把年龄范围从0~150岁设置到了10~145岁。如果你不希望改变其他设置比如这里的years old将原有的值重新写一遍就可以了。
- 注意如果控件名称不存在则返回None。注意做好类型判断。
### 禁用和启用单个控件(设置灰色)
```python
views = [('line_edit', 'name', 'What\'s your name?', 'hzy'),
('number', 'age', 'How old are you?', 88, 'years old', (0, 150))]
sp = SettingsPanel(views=views, layout_dir='v')
sp.get_ctrl('name').setEnabled(False) # 禁用name控件
sp.get_ctrl('name').setEnabled(True) # 启用name控件
```
示例:
```python
import sys
from PyQt5.QtWidgets import QApplication
from pmgwidgets import SettingsPanel
if __name__ == '__main__':
app = QApplication(sys.argv)
# 类型;名称;显示的提示文字;初始值;//单位;范围
views = [('line_edit', 'name', 'What\'s your name?', 'hzy'),
('number', 'age', 'How old are you?', 88, 'years old', (0, 150)),
('number', 'height', 'How High could This Plane fly?', 12000, 'm', (10, 20000)),
('bool', 'sport', 'do you like sport', True),
('choose_box', 'plane_type', 'plane type', 'f22', ['f22', 'f18', 'j20', 'su57'],
['f22战斗机', 'f18战斗轰炸机', 'j20战斗机', 'su57战斗机']),
('color', 'color', 'Which color do u like?', (0, 200, 0))]
sp = SettingsPanel(views=views, layout_dir='v')
sp.widgets_dic['plane_type'].set_choices(['aaa', 'vvvvv', 'xxxxxx'])
sp.set_items(views[3:6])
sp.show()
sp2 = SettingsPanel(views=views, layout_dir='h')
sp2.show()
sp2.setMaximumHeight(30)
val = sp.get_value() # 返回一个字典。初始值为表格的第二列:第四列。
print(val)
sys.exit(app.exec_())
```
## 文件树控件
class PMGFilesTreeview(QTreeView):
### 如何插入界面
def __init__(self, initial_dir: str = '', parent=None):
initial_dir:str,初始时的路径。
parent:父控件可以为None。
### 信号:
- new_file_signal = pyqtSignal(str)
新建文件信号,返回一个参数,是新建文件的绝对路径
- new_folder_signal = pyqtSignal(str)
新建文件夹信号,返回一个参数,是新建文件夹的绝对路径
- delete_file_signal = pyqtSignal(str)
删除文件或者文件夹信号,返回一个参数,是文件夹的绝对路径。
- rename_file_signal = pyqtSignal(str, str)
文件重命名的信号,返回两个参数,分别是重命名之前的绝对路径和重命名之后的绝对路径。
注意:以上信号都是只有操作成功才会被触发的。**如果操作不成功(比如重命名时存在相同文件、删除文件时权限不够),那么就不会触发。**。
## 容器控件
### 流式布局控件PMFlowArea
流式布局控件为PMFlowArea示例见tests文件夹的flow_layout_widget.py。
运行这个例子可以发现以下效果:
![](doc_figures/pmflowarea_1.png)
![](doc_figures/pmflowarea_2.png)
可以看到,布局在界面左右拖拽的时候,按钮会自动重排。
问题控件库的按钮自动重排之前似乎不是从0,0开始添加按钮的。
### 选项卡控件PMGTabWidget
(这里名字不对!需要改过来!!)
是选项卡控件。
选项卡控件的特点是它的setup_ui方法中会调用子界面的setup_ui方法。同理也适用于bind_events。
### 可停靠控件PMGDockWidget
额外定义了raise_into_view的方法调用这方法时可以保证此控件提升到窗口最顶端。
[TODO]:需要考虑将窗口也增设进来!
## 相关函数和方法
### 文件操作
#### rename_file(prev_absolute_path:str, new_absolute_path:str)->bool
重命名文件或者文件夹
prev_absolute_path:之前的绝对路径
new_absolute_path:新的绝对路径
返回值True为操作成功False为不成功比如已有文件或者文件夹与新的名称重名
#### move_to_trashpath:str->bool
path:要移到回收站的文件夹的绝对路径。
返回值True为操作成功False为不成功。
### 执行系统命令
[!TODO]
#### run_command_in_terminal(打开系统终端并在其中执行命令。)
在终端命令行中运行命令。
```
from pmgwidgets import run_command_in_terminal
def test_run_in_terminal():
import time
run_command_in_terminal('dir', close_mode='no')
time.sleep(1)
run_command_in_terminal('dir', close_mode='wait_key')
time.sleep(1)
run_command_in_terminal('dir', close_mode='auto')
test_run_in_terminal()
```
close_mode的意思时命令执行完之后终端怎么做。当其为no'的时候,终端不退出,可以输入命令继续执行下一条;显示为'wait_key'的时候终端等待按任意键退出显示为auto的时候终端执行完之后就退出——所以执行dir一类秒完成的命令就会闪现一下然后便不见了。
# pmgwindows常用控件介绍

View File

@ -1,12 +0,0 @@
from .table import PMGTableWidget, PMTableView, PMGTableTabWidget, PMGTableViewer
from .containers import PMFlowArea, PMScrollArea, PMTabWidget, PMFlowLayout, PMFlowLayoutWithGrid, \
PMGDockWidget
from .basicwidgets import PMDockObject, PMPushButtonPane, PMToolButton
from .toolbars import PMGToolBar, TopToolBar, ActionWithMessage
from .sourcemgr import create_icon
from .normal import SettingsPanel, center_window, set_always_on_top, set_closable, set_minimizable
from .views import PMGFilesTreeview, PMFileSystemModel, PMJsonTreeViewer
from .display import *
from .platform import *
from .communication import *
from .communication.test.clientgeneral import GeneralClient

View File

@ -1,3 +0,0 @@
from .pmpushbuttons import PMPushButtonPane
from .toolbutton import PMToolButton
from .object import PMDockObject

View File

@ -1,34 +0,0 @@
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QScrollArea
from PyQt5.QtCore import Qt
class PMScrollableLabel(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
layout = QVBoxLayout()
self.scroll_area = QScrollArea()
self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.label = QLabel()
layout_label = QVBoxLayout()
layout_label.addWidget(self.label)
self.scroll_area.setLayout(layout_label)
layout.addWidget(self.scroll_area)
self.setLayout(layout)
self.setText = self.label.setText
self.setWordWrap = self.label.setWordWrap
if __name__ == '__main__':
from PyQt5.QtWidgets import QApplication
import sys
app = QApplication(sys.argv)
w = PMScrollableLabel()
w.setWordWrap(True)
w.setText('aaaaaaaaa ' * 10000)
w.show()
sys.exit(app.exec_())

View File

@ -1,43 +0,0 @@
from typing import Tuple
from PyQt5.QtWidgets import QWidget
class PMDockObject(object):
"""
修改
原先主窗口中的各个可停靠窗口在点击右上角关闭按钮的时候会隐藏可以在视图菜单中打开
但是当控件中有on_closed_action属性且值为delete的时候控件就会被回收
为了实现控件的管理控件需要继承PMDockObject并且需要用多继承的方式
from pyminer2.ui.generalwidgets import PMDockObject
这个PMDockObject中定义了一些方法作为补充
class PMDockObject(object):
on_closed_action = 'hide' # 或者'delete'。
def raise_widget_to_visible(self, widget: 'QWidget'):
pass
def on_dock_widget_deleted(self):
pass
"""
on_closed_action = 'hide' # 或者'delete'。
def is_temporary(self)->bool:
"""
如果为True,相应的控件将在窗口关闭时删除并且不会记忆其位置如果为False则相应的控件不会被删除且其位置将被记忆
默认返回False
"""
return False
def raise_widget_to_visible(self, widget: 'QWidget'):
pass
def on_dock_widget_deleted(self):
pass
def get_split_portion_hint(self) -> Tuple[int, int]:
return (None, None)

View File

@ -1,63 +0,0 @@
"""
pmpushbuttons是一个按钮组专门负责向工具栏中插入按钮
其中PMPushButtonPane是按钮的载体可以插入竖向排布的两个到三个按钮
作者Zhanyi Hou
"""
from typing import List
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QMenu, QLabel
from pmgwidgets.sourcemgr import create_icon
class PMPushButtonPane(QWidget):
def __init__(self):
super().__init__()
self.layout = QVBoxLayout()
self.setLayout(self.layout)
def add_height_occu_buttons(self):
button_num = 3
btn_list = []
for i in range(button_num):
btn = QLabel()
btn.setText(' ')
btn.setProperty('qssp', 'occu')
btn_list.append(btn)
btn.setObjectName('space_occupation_button')
self.layout.addWidget(btn)
return btn_list
def add_buttons(self, button_num: int = 2, text: list = None, icon_path: list = None,
menu: list = None) -> List[QPushButton]:
if text is None:
text = [''] * button_num
if icon_path is None:
icon_path = [None] * button_num
if menu is None:
menu = [None] * button_num
if len(text) != button_num or len(
icon_path) != button_num or len(menu) != button_num:
raise Exception('text,icon和menu参数都必须为长为2的可迭代对象。')
qssproperty = "minibutton3"
if button_num == 2:
qssproperty = "minibutton2"
btn_list = []
for i in range(button_num):
btn = self.add_button(text=text[i], icon=create_icon(icon_path[i]), menu=menu[i], qssproperty=qssproperty)
btn_list.append(btn)
return btn_list
def add_button(self, text: str = '', icon: QIcon = None, menu: QMenu = None, qssproperty = "minibutton3") \
-> QPushButton:
pbtn = QPushButton()
pbtn.setText(text)
if icon is not None:
pbtn.setIcon(icon)
if menu is not None:
pbtn.setMenu(menu)
pbtn.setProperty("qssp", qssproperty)
self.layout.addWidget(pbtn)
return pbtn

View File

@ -1,77 +0,0 @@
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from PyQt5.QtCore import QEvent, QTimer
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import QToolButton, QGraphicsDropShadowEffect, QMenu, QWidget, QHBoxLayout
class PMMenu(QMenu):
def __init__(self, tool_button: 'PMToolButton' = None):
super().__init__()
self.tool_button = tool_button
def leaveEvent(self, a0: QEvent) -> None:
print('leave menu', self.underMouse(), self.tool_button.underMouse())
# timer = QTimer()
QTimer.singleShot(50, self.tool_button.hide_menu)
# if self.tool_button.underMouse():
# return
# else:
# self.hide()
class PMToolButton(QToolButton):
shadow = QGraphicsDropShadowEffect() # 实例阴影
def __init__(self, parent=None):
super().__init__(parent)
self.shadow.setColor(QColor(63, 72, 204)) # 设置阴影颜色
self.shadow.setOffset(0, 0) # 设置阴影方向
self.setMinimumWidth(60)
self.setMinimumHeight(40)
self.clicked.connect(self.set_btn_clicked_effect)
self.menu = PMMenu(tool_button=self)
self.setMenu(self.menu)
self.menu.addAction('新建行')
self.menu.addAction('新建列')
self.menu.addAction('删除行')
self.menu.addAction('删除列')
# self.setFocusPolicy()
# self.setContextMenuPolicy(Qt.CustomContextMenu)
# self.setPopupMode(QToolButton.InstantPopup)
def set_btn_clicked_effect(self):
# 设置模糊度并为按钮添加阴影
self.shadow.setBlurRadius(20)
self.setGraphicsEffect(self.shadow)
def unset_btn_clicked_effect(self):
# 设置模糊度为0 间接取消阴影
self.shadow.setBlurRadius(0)
def show_menu(self):
if self.underMouse() or self.menu.underMouse():
self.menu.popup(self.mapToGlobal(self.pos()))
self.grabMouse()
def hide_menu(self):
if not (self.underMouse() or self.menu.underMouse()):
self.menu.hide()
# self.clearFocus()
if __name__ == '__main__':
from PyQt5.QtWidgets import QApplication
import sys
app = QApplication(sys.argv)
w = QWidget()
layout = QHBoxLayout()
w.setLayout(layout)
for i in range(3):
w1 = PMToolButton()
layout.addWidget(w1)
w1.setText('aaa')
w.show()
sys.exit(app.exec_())

View File

@ -1 +0,0 @@
from pmgwidgets.communication.test import *

View File

@ -1,22 +0,0 @@
if __name__=='__main__':
from pmgwidgets import BaseClient
import numpy as np
x = np.random.random(10) + np.linspace(1, 10, 10)
y = np.random.random(10) + np.linspace(1, 10, 10)
print(x)
c = BaseClient()
print('set_var')
c.set_var('x', x)
c.set_var('y', y)
c.set_var('z', y)
c.set_var('w', y)
c.set_var('t', y)
c.set_var('y', y)
c.get_var('x')
print(c.get_all_vars())
print(c.get_all_var_names())
# while (1):
# time.sleep(1)

View File

@ -1,5 +0,0 @@
from pmgwidgets import GeneralClient
import time
__client = GeneralClient()
while(1):
time.sleep(1)

View File

@ -1 +0,0 @@
from pmgwidgets.communication.test.baseclient import BaseClient, get_style_sheet

View File

@ -1,222 +0,0 @@
import socket
import cloudpickle
import base64
import sys
import json
def get_style_sheet() -> str:
return BaseClient().get_style_sheet()
class BaseClient():
def init_socket(self, port: int = 12306):
host = 'localhost'
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) # 在客户端开启心跳维护
client.connect((host, port))
return client
def get_var(self, data_name: str):
"""
从工作空间获取数据
:param data_name:
:return:
"""
payload = {
"method": "read_p",
"params": [data_name],
"jsonrpc": "2.0",
"id": 0,
}
b = self.request_data(json.dumps(payload).encode('utf-8'))
response_dic: dict = json.loads(b.decode('utf-8'))
data_b64 = response_dic.get(data_name)
if data_b64 is not None:
s = self.load_data_from_b64(data_b64)
return s
return None
def dump_data_from_b64(self, data: object) -> str:
try:
data_dumped_bytes = cloudpickle.dumps(data)
return base64.b64encode(data_dumped_bytes).decode('ascii')
except:
pass
def load_data_from_b64(self, data_b64: str) -> object:
try:
return cloudpickle.loads(base64.b64decode(data_b64))
except:
import traceback
traceback.print_exc()
return None
def delete_var(self, var_name: list, provider: str):
payload = {
"method": "delete_variable",
"params": [var_name, provider],
"jsonrpc": "2.0",
"id": 0,
}
# print('set_data', 'set_data')
payload_byte = json.dumps(payload).encode('utf-8')
b = self.request_data(payload_byte)
# print('get_all_data', b)
response_dic: dict = json.loads(b.decode('utf-8'))
if response_dic['message'] == 'succeed':
return response_dic['var_names']
else:
return []
def get_all_public_var_names(self):
payload = {
"method": "get_all_public_variable_names",
"params": [],
"jsonrpc": "2.0",
"id": 0,
}
payload_byte = json.dumps(payload).encode('utf-8')
b = self.request_data(payload_byte)
response_dic: dict = json.loads(b.decode('utf-8'))
if response_dic['message'] == 'succeed':
return response_dic['var_names']
else:
return []
def get_all_var_names(self):
payload = {
"method": "get_all_variable_names",
"params": [],
"jsonrpc": "2.0",
"id": 0,
}
payload_byte = json.dumps(payload).encode('utf-8')
b = self.request_data(payload_byte)
response_dic: dict = json.loads(b.decode('utf-8'))
if response_dic['message'] == 'succeed':
return response_dic['var_names']
else:
return []
def set_var_dic(self, var_dic: dict, provider: str = 'server'):
# print(var_dic)
dic = {}
for k in var_dic.keys():
try:
b64 = self.dump_data_from_b64(var_dic[k])
dic[k] = b64
except:
import traceback
traceback.print_exc()
pass
dumped_data = self.dump_data_from_b64(dic)
payload = {
"method": "write_var_dic",
"params": [dumped_data, provider],
"jsonrpc": "2.0",
"id": 0,
}
payload_byte = json.dumps(payload).encode('utf-8')
b = self.request_data(payload_byte)
response_dic: dict = json.loads(b.decode('utf-8'))
return response_dic
def get_all_vars(self):
payload = {
"method": "get_var_dic",
"params": [],
"jsonrpc": "2.0",
"id": 0,
}
payload_byte = json.dumps(payload).encode('utf-8')
b = self.request_data(payload_byte)
response_dic: dict = json.loads(b.decode('utf-8'))
if response_dic.get('var_dic') != None:
b = response_dic.get('var_dic')
return self.load_data_from_b64(b)
else:
return response_dic
def set_var(self, data_name: str, data: object, provider: str = 'server'):
"""
向数据管理类中写入数据
:param data_name:
:param data:
:param provider:
:return:
"""
if sys.getsizeof(data) / (1024 * 1024) > 150:
raise MemoryError('Data \'%s\' size %f MB is larger than limit ( 150MB ) .' % (
data_name, sys.getsizeof(data) / 1024 / 1024))
dumped_data = self.dump_data_from_b64(data)
message = 'Data size after b64 encode %f MB is too large,it should less than 300MB after base64 encoded.\n ' % (
sys.getsizeof(dumped_data) / 1024 / 1024)
assert sys.getsizeof(
dumped_data) / 1024 / 1024 <= 300, MemoryError(message)
payload = {
"method": "write_p",
"params": [data_name, dumped_data, provider],
"jsonrpc": "2.0",
"id": 0,
}
payload_byte = json.dumps(payload).encode('utf-8')
b = self.request_data(payload_byte)
response_dic: dict = json.loads(b.decode('utf-8'))
return response_dic
def get_settings(self) -> dict:
"""
获取主界面的设置项
:return:
"""
payload = {
"method": "get_settings",
"params": [],
"jsonrpc": "2.0",
"id": 0,
}
b = self.request_data(json.dumps(payload).encode('utf-8'))
result_dic: dict = json.loads(b.decode('utf-8')).get('settings')
assert result_dic is not None
return result_dic
def get_style_sheet(self) -> str:
"""
获取主界面的样式表
:return:
"""
payload = {
"method": "get_style_sheet",
"params": [],
"jsonrpc": "2.0",
"id": 0,
}
b = self.request_data(json.dumps(payload).encode('utf-8'))
result = json.loads(b.decode('utf-8')).get('style_sheet')
assert result is not None
return result
def request_data(self, byte_data) -> bytes:
HOST = '127.0.0.1'
PORT = 12306
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 定义socket类型网络通信TCP
s.connect((HOST, PORT)) # 要连接的IP与端口
s.send(b'request_data')
msg = s.recv(1024)
s.sendall(byte_data + b'PMEND') # 把命令发送给对端
l = []
while (1):
b = s.recv(65536)
l.append(b)
if b.endswith(b'PMEND'):
# p = p.strip(b'PMEND')
l[-1] = l[-1].strip(b'PMEND')
break
data = b''.join(l) # 把接收的数据定义为变量
s.close() # 关闭连接
return data

View File

@ -1,122 +0,0 @@
# coding=utf-8
__author__ = '药师Aric'
import base64
import json
import zlib
from typing import List
import logging
logger = logging.getLogger(__name__)
import cloudpickle
'''
client端
长连接短连接心跳
'''
import socket
import time
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import QObject, QThread, pyqtSignal, QTimer
import sys
from pmgwidgets.communication.test.util import timeit, receive
from pmgwidgets.communication.test.baseclient import BaseClient
def parse_splicing_packets(packet_bytes: bytes) -> List[bytes]:
return packet_bytes.split(b'PMEND')
class RecvWork(QObject):
signal_received = pyqtSignal(bytes)
def __init__(self, socket_client: socket.socket):
super(RecvWork, self).__init__()
self.quit = False
self.socket_client = socket_client
def work(self):
client = self.socket_client
client.sendall(b'long_conn')
while True:
try:
recv = receive(client)
assert recv != b''
packets = parse_splicing_packets(recv)
for p in packets:
self.signal_received.emit(p)
except:
import traceback
traceback.print_exc()
break
# time.sleep(1) # 如果想验证长时间没发数据SOCKET连接会不会断开则可以设置时间长一点
self.thread().quit()
def close_socket(self):
self.socket_client.close()
class PMClient(QObject, BaseClient):
signal_server_message_received = pyqtSignal(dict)
signal_data_changed = pyqtSignal(str)
def __init__(self, parent=None):
super().__init__(parent)
self.client = self.init_socket(12306)
self.thread_recv = QThread()
self.worker_recv = RecvWork(self.client)
self.signal_received = self.worker_recv.signal_received
self.worker_recv.signal_received.connect(self.on_server_message_received)
self.worker_recv.moveToThread(self.thread_recv)
self.thread_recv.started.connect(self.worker_recv.work)
self.thread_recv.start()
def shut_down(self):
logger.info('client quit')
self.worker_recv.close_socket()
self.thread_recv.quit()
self.thread_recv.wait(500)
def on_server_message_received(self, packet: bytes):
try:
dic = json.loads(packet)
logger.info(dic)
msg = dic.get('message')
if msg == 'data_changed':
data_name = dic.get('data_name')
if data_name is not None:
self.signal_data_changed.emit(data_name)
self.signal_server_message_received.emit(dic)
except:
import traceback
traceback.print_exc()
pass
def send_bytes(self, packet: bytes):
self.client.sendall(packet)
@timeit
def compress(self, byte_str):
return zlib.compress(byte_str)
if __name__ == '__main__':
app = QApplication(sys.argv)
import numpy as np
x = np.random.random(10) + np.linspace(1, 10, 10)
y = np.random.random(10) + np.linspace(1, 10, 10)
print(x)
c = PMClient()
print('set_var')
c.set_var('x', x)
c.set_var('y', y)
c.get_var('x')
print(c.get_all_var_names())
timer = QTimer()
timer.start(10000)
timer.timeout.connect(lambda: c.get_var('x'))
c.shut_down()
sys.exit(app.exec_())

View File

@ -1,85 +0,0 @@
# coding=utf-8
__author__ = '药师Aric'
import json
import threading
import zlib
from typing import List
'''
client端
长连接短连接心跳
'''
import socket
import time
import sys
from pmgwidgets.communication.test.util import timeit, receive
from pmgwidgets.communication.test.baseclient import BaseClient
def parse_splicing_packets(packet_bytes: bytes) -> List[bytes]:
return packet_bytes.split(b'PMEND')
class GeneralClient(BaseClient):
def __init__(self):
self.client = self.init_socket(12306)
th = threading.Thread(target=self.run_event_loop)
th.setDaemon(True)
th.start()
def shut_down(self):
print('quit')
self.client.close()
def on_server_message_received(self, packet: bytes):
try:
dic = json.loads(packet)
msg = dic.get('message')
if msg == 'data_changed':
pass
except:
import traceback
traceback.print_exc()
pass
@timeit
def compress(self, byte_str):
return zlib.compress(byte_str)
def run_event_loop(self):
self.client.sendall(b'long_conn')
while True:
try:
recv = receive(self.client)
packets = parse_splicing_packets(recv)
for packet in packets:
self.on_server_message_received(packet)
except:
import traceback
traceback.print_exc()
break
if __name__ == '__main__':
import numpy as np
x = np.random.random(10) + np.linspace(1, 10, 10)
y = np.random.random(10) + np.linspace(1, 10, 10)
print(x)
c = GeneralClient()
print('set_var')
c.set_var('x', x)
c.set_var('y', y)
c.set_var('z', y)
c.set_var('w', y)
c.set_var('t', y)
c.set_var('y', y)
c.get_var('x')
print(c.get_all_vars())
print(c.get_all_var_names())
while (1):
time.sleep(1)

View File

@ -1,207 +0,0 @@
"""
基于qthread的服务器
直接基于socket
"""
import json
from typing import List, Tuple
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import QObject, QThread, QTimer, pyqtSignal
import socket # 导入 socket 模块
import sys
import logging
from pmgwidgets.communication.test.util import receive, timeit
logger = logging.getLogger(__name__)
g_conn_pool = []
def parse_splicing_packets(packet_bytes: bytes) -> List[bytes]:
return packet_bytes.split(b'PMEND')
class MessageWork(QObject):
signal_message_received = pyqtSignal(socket.socket, bytes)
signal_work_finished = pyqtSignal()
signal_socket_closed = pyqtSignal(socket.socket)
def __init__(self, server: 'PMGServer'):
super(MessageWork, self).__init__()
self.server = server
def work(self):
self.message_handle(client=self.client)
self.signal_work_finished.emit()
def handle_packet(self, client: socket.socket, packet: bytes):
try:
dic = json.loads(packet)
func = self.server.dispatcher_dic[dic['method']]
args = dic['params']
client.sendall(func(*args).encode('utf-8') + b'PMEND')
except:
client.close()
self.signal_socket_closed.emit(client)
logger.warning(repr(self.server.long_conn_sockets))
return b''
def message_handle(self, client):
"""
消息处理
"""
while True:
logger.info('client waiting!' + repr(client))
logger.info('conn_pool_length:%d ,list is: %s' % (len(self.server.long_conn_sockets),
repr(self.server.long_conn_sockets)))
try:
bytes = receive(client)
for packet in parse_splicing_packets(bytes):
self.handle_packet(client, packet)
except:
import traceback
traceback.print_exc()
client.close()
# 删除连接
self.signal_socket_closed.emit(client)
logger.info("客户端%s下线了。" % repr(client))
break
if len(bytes) == 0:
client.close()
self.signal_socket_closed.emit(client)
logger.info("客户端%s下线了。" % repr(client))
break
class LoopWork(QObject):
def __init__(self, server_obj: 'PMGServer', server_socket: socket.socket):
super().__init__()
self.server_socket = server_socket
self.server_obj = server_obj
self.threads = []
def work(self):
self.accept_client(self.server_socket)
def accept_client(self, server_socket):
"""
接收新连接
"""
while True:
try:
client, _ = server_socket.accept() # 阻塞,等待客户端连接
except:
import traceback
traceback.print_exc()
break
# 加入连接池
conn_message = client.recv(1024)
if conn_message == b'long_conn': # 如果请求的是一个长连接,就给每个客户端创建一个独立的线程进行管理
self.server_obj.long_conn_sockets.append(client)
thread = QThread()
worker = MessageWork(self.server_obj)
worker.signal_socket_closed.connect(self.server_obj.remove_socket)
worker.client = client
# worker.server = self.server_obj
worker.moveToThread(thread)
thread.started.connect(worker.work)
thread.start()
worker.signal_work_finished.connect(thread.quit)
self.threads.append([thread, worker]) # 保持多连接要求保持引用。
else: # 如果请求的不是一个长连接,就直接进行处理,
client.send(b'conn_established')
b = receive(client)
self.handle_packet(client, b)
# except:
# import traceback
# traceback.print_exc()
# client.close()
# break
def handle_packet(self, client: socket.socket, packet: bytes):
try:
dic = json.loads(packet)
func = self.server_obj.dispatcher_dic[dic['method']]
args = dic['params']
vy = func(*args).encode('utf-8') + b'PMEND'
client.sendall(vy)
except:
import traceback
traceback.print_exc()
client.close()
logger.warning(repr(g_conn_pool))
return b''
class PMGServer(QObject):
def __init__(self, address=Tuple[str, int], parent=None):
super().__init__(parent)
self.dispatcher_dic = {}
self.long_conn_sockets = []
self.socket = self.init_socket(address)
self.server_loop_thread = QThread()
self.loop_worker = LoopWork(self, self.socket)
self.loop_worker.moveToThread(self.server_loop_thread)
self.server_loop_thread.started.connect(self.loop_worker.work)
self.server_loop_thread.start()
def init_socket(self, address):
"""
初始化套接字
"""
g_socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建 socket 对象
# g_socket_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
g_socket_server.bind(address)
g_socket_server.listen(5) # 最大等待数(有很多人理解为最大连接数,其实是错误的)
logger.info("服务端已启动,等待客户端连接...")
return g_socket_server
def broadcast_message(self, message_dic: dict = None):
"""
广播信息
message:传递信息
# 'DATA_CHANGED'
# 'SHUT_DOWN'
:param clients:
:return:
"""
if message_dic is None:
message_dic = {'name': 'broadcast', 'message': 'Are you alive?'}
clients = self.long_conn_sockets
ids = []
logger.info('broadcast message:'+repr(message_dic))
for i, client in enumerate(clients):
try:
client.sendall(json.dumps(message_dic).encode('utf8') + b'PMEND')
except:
import traceback
traceback.print_exc()
ids.append(client)
logger.info('died connections:' + repr(ids))
for not_used_socket in ids:
self.remove_socket(not_used_socket)
def remove_socket(self, socket_to_remove: socket.socket):
if socket_to_remove in self.long_conn_sockets:
self.long_conn_sockets.remove(socket_to_remove)
if __name__ == '__main__':
app = QApplication(sys.argv)
ADDRESS = ('127.0.0.1', 12306) # 绑定地址
s = PMGServer(ADDRESS)
qtimer = QTimer()
qtimer.start(2000)
qtimer.timeout.connect(lambda: s.broadcast_message(None))
sys.exit(app.exec_())

View File

@ -1,29 +0,0 @@
import time
import socket
from typing import Callable
def receive(socket_obj: socket.socket) -> bytes:
l = []
while (1):
try:
b = socket_obj.recv(1024)
except:
import traceback
traceback.print_exc()
return b''
l.append(b)
if b.endswith(b'PMEND'):
l[-1] = l[-1].strip(b'PMEND')
break
return b''.join(l)
def timeit(func):
def wrapper(*args, **kwargs):
t0 = time.time()
r = func(*args, **kwargs)
print('time_elapsed', time.time() - t0)
return r
return wrapper

View File

@ -1,22 +0,0 @@
from PyQt5.QtWidgets import QTabWidget, QWidget
class PMTabWidget(QTabWidget):
def setup_ui(self):
for tab_id in range(self.count()): # 遍历所有的tab
w = self.widget(tab_id)
if hasattr(w, 'setup_ui'):
self.widget(tab_id).setup_ui()
def addScrolledAreaTab(self, widget: QWidget, a1: str) -> int:
"""
添加使用QScrollArea包裹的Tab
:param widget:
:param a1:
:return:
"""
from pmgwidgets import PMScrollArea
scroll = PMScrollArea()
scroll.setWidget(widget)
super().addTab(scroll, a1)

View File

@ -1,17 +0,0 @@
from PyQt5.QtWidgets import QDockWidget, QMainWindow
class PMGDockWidget(QDockWidget):
def __init__(self, name, text='', parent: QMainWindow = None):
super().__init__(text, parent)
self.parent = parent
self.name = name
def raise_into_view(self):
"""
将控件提升到能直接看到的位置特别适用于两个选项卡叠在一起的情况
:return:
"""
self.setVisible(True)
self.setFocus()
self.raise_()

View File

@ -1,16 +0,0 @@
hint = 'However if your program needn\'t this package,this warning is neglectable.'
try:
from .matplotlib.qt5agg import PMMatplotlibQt5Widget
except ModuleNotFoundError:
import warnings
warnings.warn('matplotlib is not installed. ' + hint)
pass
try:
from .browser.browser import QWebEngineView,PMGWebBrowser,PMGWebEngineView
except ModuleNotFoundError:
import warnings
warnings.warn('QWebengine is not installed. ' + hint)
pass
from .editor import CythonLexer

View File

@ -1,79 +0,0 @@
"""
代码来源
https://www.cnblogs.com/taostaryu/p/9772492.html
"""
import sys
from PyQt5.QtCore import QUrl, pyqtSignal
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QVBoxLayout, QToolBar, QLineEdit
class PMGWebBrowser(QWidget):
def __init__(self, parent=None, toolbar='standard'):
"""
:param parent:
:param toolbar:多种选项no,standard,'no_url_input'
"""
super().__init__(parent)
self.webview = PMGWebEngineView()
# self.webview.load(QUrl("https://cn.bing.com"))
self.setLayout(QVBoxLayout())
self.toolbar = QToolBar()
self.url_input = QLineEdit()
# self.url_input.setText('https://cn.bing.com')
# self.load_url()
self.toolbar.addWidget(self.url_input)
self.toolbar.addAction('go').triggered.connect(lambda b: self.load_url())
self.toolbar.addAction('back').triggered.connect(self.webview.back)
self.toolbar.addAction('forward').triggered.connect(self.webview.forward)
self.layout().addWidget(self.toolbar)
if toolbar == 'no':
self.toolbar.hide()
elif toolbar == 'no_url_input':
self.url_input.hide()
self.layout().addWidget(self.webview)
self.setWindowTitle('My Browser')
self.showMaximized()
# command:>
# jupyter notebook --port 5000 --no-browser --ip='*' --NotebookApp.token=''
# --NotebookApp.password='' c:\users\12957\
# self.webview.load(QUrl("http://127.0.0.1:5000/notebooks/desktop/Untitled.ipynb")) # 直接请求页面。
# self.webview.load(QUrl("E:\Python\pyminer_bin\PyMiner\bin\pmgwidgets\display\browser\show_formula.html")) # 直接请求页面。
# self.setCentralWidget(self.webview)
def load_url(self, url: str = ''):
if url == '':
url = self.url_input.text().strip()
# print('',url)
else:
self.url_input.setText(url)
self.webview.load(QUrl(url))
class PMGWebEngineView(QWebEngineView):
windowList = []
signal_new_window_created = pyqtSignal(PMGWebBrowser)
# 重写createwindow()
def createWindow(self, QWebEnginePage_WebWindowType):
# new_webview = WebEngineView()
new_window = PMGWebBrowser()
# new_window.setCentralWidget(new_webview)
# new_window.show()
self.windowList.append(new_window) # 注:没有这句会崩溃!!!
self.signal_new_window_created.emit(new_window)
return new_window.webview
if __name__ == "__main__":
app = QApplication(sys.argv)
w = PMGWebBrowser()
w.show()
sys.exit(app.exec_())

View File

@ -1,98 +0,0 @@
# Copyright (c) Jupyter Development Team, Spyder Project Contributors.
# Distributed under the terms of the Modified BSD License.
"""Entry point for server rendering notebooks for Spyder."""
import os
# from jinja2 import FileSystemLoader
# from notebook.base.handlers import IPythonHandler, FileFindHandler
from notebook.notebookapp import flags, NotebookApp
from notebook.utils import url_path_join as ujoin
from traitlets import Bool
HERE = os.path.dirname(__file__)
flags['dark'] = (
{'SpyderNotebookServer': {'dark_theme': True}},
'Use dark theme when rendering notebooks'
)
class NotebookHandler(IPythonHandler):
"""
Serve a notebook file from the filesystem in the notebook interface
"""
# def get(self, filename):
# """Get the main page for the application's interface."""
# # Options set here can be read with PageConfig.getOption
# config_data = {
# # Use camelCase here, since that's what the lab components expect
# 'baseUrl': self.base_url,
# 'token': self.settings['token'],
# 'darkTheme': self.settings['dark_theme'],
# 'notebookPath': filename,
# 'frontendUrl': ujoin(self.base_url, 'static/'),
# # FIXME: Don't use a CDN here
# 'mathjaxUrl': 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/'
# '2.7.5/MathJax.js',
# 'mathjaxConfig': "TeX-AMS_CHTML-full,Safe"
# }
# return self.write(
# self.render_template(
# 'index.html',
# static=self.static_url,
# base_url=self.base_url,
# config_data=config_data
# )
# )
#
# def get_template(self, name):
# loader = FileSystemLoader(HERE)
# return loader.load(self.settings['jinja2_env'], name)
class SpyderNotebookServer(NotebookApp):
"""Server rendering notebooks in HTML and serving them over HTTP."""
flags = flags
dark_theme = Bool(
False, config=True,
help='Whether to use dark theme when rendering notebooks')
def init_webapp(self):
"""Initialize tornado webapp and httpserver."""
self.tornado_settings['dark_theme'] = self.dark_theme
super().init_webapp()
print(self.base_url)
url = ujoin(self.base_url, r'/notebook/(.*)')
print(url)
# default_handlers = [
# (url, NotebookHandler),
# (ujoin(self.base_url, r"/static/(.*)"), FileFindHandler,
# {'path': os.path.join(HERE, 'build')})
# ]
# self.web_app.add_handlers('.*$', default_handlers)
def run():
import time
from IPython import get_ipython
while (1):
time.sleep(2)
_Jupyter = get_ipython()
print(_Jupyter)
if __name__ == '__main__':
# import threading
# th = threading.Thread(target=run)
# th.setDaemon(True)
# th.start()
SpyderNotebookServer.launch_instance(argv=['--port', '5000', r'c:\users\12957\desktop'])

View File

@ -1,12 +0,0 @@
def register_web_browser():
import webbrowser
webbrowser.register("qtbrowser", None,
webbrowser.GenericBrowser(
r"python E:\Python\pmgwidgetsproj\pmgwidgets\display\browser\browser.py"))
# c.NotebookApp.browser = 'qtbrowser'
webbrowser.get('qtbrowser').open_new('https://cn.bing.com')
register_web_browser()

View File

@ -1,15 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>MathJax TeX Test Page</title>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({tex2jax:
{inlineMath: [['$','$'], ['\\(','\\)']]}});
</script>
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>
</head>
<body>$$ TPM_{i} = \frac{ \frac{ N_{i} }{ L_{i} } * 1000000}{\sum_{i=1}^{n} \frac{N_{i}}{L_{i}}}$$
</body>
</html>

View File

@ -1,5 +0,0 @@
import sys
from pyqtgraph import examples
examples.run()

View File

@ -1 +0,0 @@
from .lexers.cythonlexer import CythonLexer

View File

@ -1,45 +0,0 @@
import sys
from PyQt5.Qsci import QsciScintilla, QsciLexerSQL, QsciLexer
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import QApplication, QStyleFactory
class PMBaseEditor(QsciScintilla):
def __init__(self, parent=None):
super().__init__(parent)
self.setFont(QFont('Source Code Pro', 12)) # Consolas
def set_lexer(self, lexer:'QsciLexer'):
self._lexer = lexer
self._lexer.setFont(self.font())
self.setLexer(self._lexer)
# def _init_lexer(self) -> None:
# """
# 初始化语法解析器
#
# :return: None
# """
# self._lexer = QsciLexerSQL(self)
# # self._lexer.setFont(self.font())
# self.setLexer(self._lexer)
if __name__ == '__main__':
from pmgwidgets.display.editor.lexers.cythonlexer import CythonLexer
app = QApplication(sys.argv)
textedit = PMBaseEditor()
textedit.setLexer(CythonLexer(textedit))
initial_text = """
cdef public struct Vehicle:
int speed
float power
cdef api void activate(Vehicle *v):
if v.speed >= 88 and v.power >= 1.21:
print("Time travel achieved")
"""
textedit.setText(initial_text)
textedit.show()
app.exec_()

View File

@ -1,212 +0,0 @@
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.Qsci import *
import re
class MyLexer(QsciLexerCustom):
def __init__(self, parent):
super(MyLexer, self).__init__(parent)
# Default text settings
# ----------------------
self.setDefaultColor(QColor("#ff000000"))
self.setDefaultPaper(QColor("#ffffffff"))
self.setDefaultFont(QFont("Consolas", 14))
# Initialize colors per style
# ----------------------------
self.setColor(QColor("#ff000000"), 0) # Style 0: black
self.setColor(QColor("#ff7f0000"), 1) # Style 1: red
self.setColor(QColor("#ff0000bf"), 2) # Style 2: blue
self.setColor(QColor("#ff007f00"), 3) # Style 3: green
# Initialize paper colors per style
# ----------------------------------
self.setPaper(QColor("#ffffffff"), 0) # Style 0: white
self.setPaper(QColor("#ffffffff"), 1) # Style 1: white
self.setPaper(QColor("#ffffffff"), 2) # Style 2: white
self.setPaper(QColor("#ffffffff"), 3) # Style 3: white
# Initialize fonts per style
# ---------------------------
self.setFont(QFont("Consolas", 14, weight=QFont.Bold), 0) # Style 0: Consolas 14pt
self.setFont(QFont("Consolas", 14, weight=QFont.Bold), 1) # Style 1: Consolas 14pt
self.setFont(QFont("Consolas", 14, weight=QFont.Bold), 2) # Style 2: Consolas 14pt
self.setFont(QFont("Consolas", 14, weight=QFont.Bold), 3) # Style 3: Consolas 14pt
def language(self):
return "SimpleLanguage"
def description(self, style):
if style == 0:
return "myStyle_0"
elif style == 1:
return "myStyle_1"
elif style == 2:
return "myStyle_2"
elif style == 3:
return "myStyle_3"
###
return ""
def styleText(self, start, end):
# 1. Initialize the styling procedure
# ------------------------------------
self.startStyling(start)
# 2. Slice out a part from the text
# ----------------------------------
text = self.parent().text()[start:end]
# 3. Tokenize the text
# ---------------------
p = re.compile(r"[*]\/|\/[*]|\s+|\w+|\W")
# 'token_list' is a list of tuples: (token_name, token_len)
token_list = [ (token, len(bytearray(token, "utf-8"))) for token in p.findall(text)]
# 4. Style the text
# ------------------
# 4.1 Check if multiline comment
multiline_comm_flag = False
editor = self.parent()
if start > 0:
previous_style_nr = editor.SendScintilla(editor.SCI_GETSTYLEAT, start - 1)
if previous_style_nr == 3:
multiline_comm_flag = True
# 4.2 Style the text in a loop
for i, token in enumerate(token_list):
if multiline_comm_flag:
self.setStyling(token[1], 3)
if token[0] == "*/":
multiline_comm_flag = False
else:
if token[0] in ["for", "while", "return", "int", "include"]:
# Red style
self.setStyling(token[1], 1)
elif token[0] in ["(", ")", "{", "}", "[", "]", "#"]:
# Blue style
self.setStyling(token[1], 2)
elif token[0] == "/*":
multiline_comm_flag = True
self.setStyling(token[1], 3)
else:
# Default style
self.setStyling(token[1], 0)
myCodeSample = r"""#include <stdio.h>
/*
* This is a
* multiline
* comment */
int main()
{
char arr[5] = {'h', 'e', 'l', 'l', 'o'};
int i;
for(i = 0; i < 5; i++) {
printf(arr[i]);
}
return 0;
}""".replace("\n","\r\n")
class CEditor():
pass
class CustomMainWindow(QMainWindow):
def __init__(self):
super(CustomMainWindow, self).__init__()
# -------------------------------- #
# Window setup #
# -------------------------------- #
# 1. Define the geometry of the main window
# ------------------------------------------
self.setGeometry(300, 300, 800, 400)
self.setWindowTitle("QScintilla Test")
# 2. Create frame and layout
# ---------------------------
self.__frm = QFrame(self)
self.__frm.setStyleSheet("QWidget { background-color: #ffeaeaea }")
self.__lyt = QVBoxLayout()
self.__frm.setLayout(self.__lyt)
self.setCentralWidget(self.__frm)
self.__myFont = QFont()
self.__myFont.setPointSize(14)
# 3. Place a button
# ------------------
self.__btn = QPushButton("Qsci")
self.__btn.setFixedWidth(50)
self.__btn.setFixedHeight(50)
self.__btn.clicked.connect(self.__btn_action)
self.__btn.setFont(self.__myFont)
self.__lyt.addWidget(self.__btn)
# -------------------------------- #
# QScintilla editor setup #
# -------------------------------- #
# ! Make instance of QSciScintilla class!
# ----------------------------------------
self.__editor = QsciScintilla()
self.__editor.setText(myCodeSample) # 'myCodeSample' is a string containing some C-code
self.__editor.setLexer(None) # We install lexer later
self.__editor.setUtf8(True) # Set encoding to UTF-8
self.__editor.setFont(self.__myFont) # Gets overridden by lexer later on
# 1. Text wrapping
# -----------------
self.__editor.setWrapMode(QsciScintilla.WrapWord)
self.__editor.setWrapVisualFlags(QsciScintilla.WrapFlagByText)
self.__editor.setWrapIndentMode(QsciScintilla.WrapIndentIndented)
# 2. End-of-line mode
# --------------------
self.__editor.setEolMode(QsciScintilla.EolWindows)
self.__editor.setEolVisibility(False)
# 3. Indentation
# ---------------
self.__editor.setIndentationsUseTabs(False)
self.__editor.setTabWidth(4)
self.__editor.setIndentationGuides(True)
self.__editor.setTabIndents(True)
self.__editor.setAutoIndent(True)
# 4. Caret
# ---------
self.__editor.setCaretForegroundColor(QColor("#ff0000ff"))
self.__editor.setCaretLineVisible(True)
self.__editor.setCaretLineBackgroundColor(QColor("#1f0000ff"))
self.__editor.setCaretWidth(2)
# 5. Margins
# -----------
# Margin 0 = Line nr margin
self.__editor.setMarginType(0, QsciScintilla.NumberMargin)
self.__editor.setMarginWidth(0, "0000")
self.__editor.setMarginsForegroundColor(QColor("#ff888888"))
# -------------------------------- #
# Install lexer #
# -------------------------------- #
self.__lexer = MyLexer(self.__editor)
self.__editor.setLexer(self.__lexer)
# ! Add editor to layout !
# -------------------------
self.__lyt.addWidget(self.__editor)
self.show()
def __btn_action(self):
print("Hello World!")
if __name__ == '__main__':
app = QApplication(sys.argv)
QApplication.setStyle(QStyleFactory.create('Fusion'))
myGUI = CustomMainWindow()
sys.exit(app.exec_())

View File

@ -1,309 +0,0 @@
# Import the PyQt5 module with some of the GUI widgets
import keyword
import PyQt5.QtWidgets
# Import the QScintilla module
import PyQt5.Qsci
# Import Python's sys module needed to get the application arguments
import sys
import re
# Create a custom Nim lexer
class CythonLexer(PyQt5.Qsci.QsciLexerCustom):
styles = {
"Default": 0,
"Keyword": 1,
"Unsafe": 2,
"MultilineComment": 3,
}
keyword_list = keyword.kwlist + ['cdef', 'cpdef', 'cimport', 'enum', 'ctypedef', 'struct',
'extern', 'api', 'public', 'private']
unsafe_keyword_list = ['int', 'float', 'bool', 'void', 'double', 'long', 'unsigned', 'char'] +\
['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__build_class__', '__import__',
'abs', 'all', 'any', 'ascii', 'bin', 'breakpoint', 'callable', 'chr', 'compile',
'delattr', 'dir', 'divmod', 'eval', 'exec', 'format', 'getattr', 'globals', 'hasattr',
'hash', 'hex', 'id', 'input', 'isinstance', 'issubclass', 'iter', 'len', 'locals',
'max', 'min', 'next', 'oct', 'ord', 'pow', 'print',
'repr', 'round', 'setattr', 'sorted', 'sum', 'vars',
'None', 'Ellipsis',
'NotImplemented',
'False',
'True',
'bool',
'memoryview',
'bytearray',
'bytes',
'classmethod',
'complex',
'dict',
'enumerate',
'filter',
'float',
'frozenset',
'property',
'int',
'list',
'map',
'object',
'range',
'reversed',
'set',
'slice',
'staticmethod',
'str',
'super',
'tuple',
'type',
'zip',
'__debug__',
'BaseException',
'Exception',
'TypeError',
'StopAsyncIteration',
'StopIteration',
'GeneratorExit',
'SystemExit',
'KeyboardInterrupt',
'ImportError',
'ModuleNotFoundError',
'OSError',
'EnvironmentError',
'IOError',
'WindowsError',
'EOFError',
'RuntimeError',
'RecursionError',
'NotImplementedError',
'NameError',
'UnboundLocalError',
'AttributeError',
'SyntaxError',
'IndentationError',
'TabError',
'LookupError',
'IndexError',
'KeyError',
'ValueError',
'UnicodeError',
'UnicodeEncodeError',
'UnicodeDecodeError',
'UnicodeTranslateError',
'AssertionError',
'ArithmeticError',
'FloatingPointError',
'OverflowError',
'ZeroDivisionError',
'SystemError',
'ReferenceError',
'MemoryError',
'BufferError',
'Warning',
'UserWarning',
'DeprecationWarning',
'PendingDeprecationWarning',
'SyntaxWarning',
'RuntimeWarning',
'FutureWarning',
'ImportWarning',
'UnicodeWarning',
'BytesWarning',
'ResourceWarning',
'ConnectionError',
'BlockingIOError',
'BrokenPipeError',
'ChildProcessError',
'ConnectionAbortedError',
'ConnectionRefusedError',
'ConnectionResetError',
'FileExistsError',
'FileNotFoundError',
'IsADirectoryError',
'NotADirectoryError',
'InterruptedError',
'PermissionError',
'ProcessLookupError',
'TimeoutError',
'open',
'copyright',
'credits',
'license',
'help',
'execfile',
'runfile',
'__IPYTHON__',
'display',
'get_ipython']
def __init__(self, parent=None):
# Initialize superclass
super().__init__(parent)
assert isinstance(parent,PyQt5.Qsci.QsciScintilla)
self.setEditor(parent)
# Set the default style values
self.setDefaultColor(PyQt5.QtGui.QColor(0x00, 0x00, 0x00))
self.setDefaultPaper(PyQt5.QtGui.QColor(0xff, 0xff, 0xff))
self.setDefaultFont(PyQt5.QtGui.QFont("Courier", 8))
# Initialize all style colors
self.init_colors()
# Init the fonts
for i in range(len(self.styles)):
if i == self.styles["Keyword"]:
# Make keywords bold
self.setFont(PyQt5.QtGui.QFont("Courier", 8, weight=PyQt5.QtGui.QFont.Black), i)
else:
self.setFont(PyQt5.QtGui.QFont("Courier", 8), i)
# Set the Keywords style to be clickable with hotspots
# using the scintilla low level messaging system
parent.SendScintilla(
PyQt5.Qsci.QsciScintillaBase.SCI_STYLESETHOTSPOT,
self.styles["Keyword"],
True
)
parent.SendScintilla(
PyQt5.Qsci.QsciScintillaBase.SCI_SETHOTSPOTACTIVEFORE,
True,
PyQt5.QtGui.QColor(0x00, 0x7f, 0xff)
)
parent.SendScintilla(
PyQt5.Qsci.QsciScintillaBase.SCI_SETHOTSPOTACTIVEUNDERLINE, True
)
# Define a hotspot click function
def hotspot_click(position, modifiers):
"""
Simple example for getting the clicked token
"""
text = parent.text()
delimiters = [
'(', ')', '[', ']', '{', '}', ' ', '.', ',', ';', '-',
'+', '=', '/', '*', '#'
]
start = 0
end = 0
for i in range(position + 1, len(text)):
if text[i] in delimiters:
end = i
break
for i in range(position, -1, -1):
if text[i] in delimiters:
start = i
break
clicked_token = text[start:end].strip()
# Print the token and replace it with the string "CLICK"
print("'" + clicked_token + "'")
parent.setSelection(0, start + 1, 0, end)
# parent.replaceSelectedText("CLICK")
# Attach the hotspot click signal to a predefined function
parent.SCN_HOTSPOTCLICK.connect(hotspot_click)
self.cython_imported = False
def init_colors(self):
# Font color
self.setColor(PyQt5.QtGui.QColor(0x00, 0x00, 0x00), self.styles["Default"])
self.setColor(PyQt5.QtGui.QColor(0x00, 0x00, 0x7f), self.styles["Keyword"])
self.setColor(PyQt5.QtGui.QColor(0x7f, 0x00, 0x00), self.styles["Unsafe"])
self.setColor(PyQt5.QtGui.QColor(0x7f, 0x7f, 0x00), self.styles["MultilineComment"])
# Paper color
for i in range(len(self.styles)):
self.setPaper(PyQt5.QtGui.QColor(0xff, 0xff, 0xff), i)
def language(self):
return "Python"
# def lexer(self) -> str:
# return 'Python'
def description(self, style):
if style < len(self.styles):
description = "Custom lexer for the Nim programming languages"
else:
description = ""
return description
def styleText(self, start, end):
if self.cython_imported == True:
self.cython_module.cython_style_text(start, end, self, self.parent())
else:
# Initialize the styling
self.startStyling(start)
# Tokenize the text that needs to be styled using regular expressions.
# To style a sequence of characters you need to know the length of the sequence
# and which style you wish to apply to the sequence. It is up to the implementer
# to figure out which style the sequence belongs to.
# THE PROCEDURE SHOWN BELOW IS JUST ONE OF MANY!
splitter = re.compile(r"(\{\.|\.\}|\#|\'|\"\"\"|\n|\s+|\w+|\W)")
# Scintilla works with bytes, so we have to adjust the start and end boundaries.
# Like all Qt objects the lexers parent is the QScintilla editor.
text = bytearray(self.parent().text(), "utf-8")[start:end].decode("utf-8")
tokens = [
(token, len(bytearray(token, "utf-8")))
for token in splitter.findall(text)
]
# Multiline styles
multiline_comment_flag = False
# Check previous style for a multiline style
editor = self.editor()
if start != 0:
previous_style = editor.SendScintilla(editor.SCI_GETSTYLEAT, start - 1)
if previous_style == self.styles["MultilineComment"]:
multiline_comment_flag = True
# Style the text in a loop
for i, token in enumerate(tokens):
if multiline_comment_flag == False and token[0] == "#" and tokens[i + 1][0] == "[":
# Start of a multiline comment
self.setStyling(token[1], self.styles["MultilineComment"])
# Set the multiline comment flag
multiline_comment_flag = True
elif multiline_comment_flag == True:
# Multiline comment flag is set
self.setStyling(token[1], self.styles["MultilineComment"])
# Check if a multiline comment ends
if token[0] == "#" and tokens[i - 1][0] == "]":
multiline_comment_flag = False
elif token[0] in self.keyword_list:
# Keyword
self.setStyling(token[1], self.styles["Keyword"])
elif token[0] in self.unsafe_keyword_list:
# Keyword
self.setStyling(token[1], self.styles["Unsafe"])
else:
# Style with the default style
self.setStyling(token[1], self.styles["Default"])
if __name__ == '__main__':
# Create the main PyQt application object
application = PyQt5.QtWidgets.QApplication(sys.argv)
# Create a QScintila editor instance
editor = PyQt5.Qsci.QsciScintilla()
# Set the lexer to the custom Nim lexer
nim_lexer = CythonLexer(editor)
editor.setLexer(nim_lexer)
# Set the initial text
initial_text = """
cdef public struct Vehicle:
int speed
float power
cdef api void activate(Vehicle *v):
if v.speed >= 88 and v.power >= 1.21:
print("Time travel achieved")
"""
# Set the editor's text to something huge
editor.setText(initial_text)
# For the QScintilla editor to properly process events we need to add it to
# a QMainWindow object.
# main_window = PyQt5.QtWidgets.QMainWindow()
# # Set the central widget of the main window to be the editor
# main_window.setCentralWidget(editor)
# # Resize the main window and show it on the screen
# main_window.resize(800, 600)
# main_window.show()
editor.show()
# Execute the application
application.exec_()

View File

@ -1,185 +0,0 @@
{ontinously_data_process.json
"nodes": {
"1": {
"text": "start",
"id": "1",
"pos": [
-435.0,
-90.0
],
"icon": "E:\\Python\\pyminer_bin\\PyMiner\\bin\\pmgwidgets\\flowchart\\icons\\logo.png",
"content": {
"code": "import time\ndef function(x=23):\r\n time.sleep(1)\n return [x]\n "
},
"input_ports": {
"1:input:1": {
"id": "1:input:1",
"pos": [
-430.0,
-50.0
],
"contents": {},
"text": "input"
}
},
"output_ports": {
"1:output:1": {
"id": "1:output:1",
"pos": [
-250.0,
-50.0
],
"contents": {},
"text": "port"
}
}
},
"3": {
"text": "x+y+z",
"id": "3",
"pos": [
245.0,
-80.0
],
"icon": "E:\\Python\\pyminer_bin\\PyMiner\\bin\\pmgwidgets\\flowchart\\icons\\logo.png",
"content": {
"code": "\nimport time\ndef function(x,y,z):\r\n time.sleep(1)\n return [x+y+z]\n "
},
"input_ports": {
"3:input:1": {
"id": "3:input:1",
"pos": [
250.0,
-60.0
],
"contents": {},
"text": "x"
},
"3:input:2": {
"id": "3:input:2",
"pos": [
250.0,
-40.0
],
"contents": {},
"text": "y"
},
"3:input:3": {
"id": "3:input:3",
"pos": [
250.0,
-20.0
],
"contents": {},
"text": "z"
}
},
"output_ports": {
"3:output:1": {
"id": "3:output:1",
"pos": [
430.0,
-40.0
],
"contents": {},
"text": "output"
}
}
},
"4": {
"text": "process\uff1ax+2",
"id": "4",
"pos": [
-100.0,
-185.0
],
"icon": "E:\\Python\\pyminer_bin\\PyMiner\\bin\\pmgwidgets\\flowchart\\icons\\logo.png",
"content": {
"code": "\nimport time\ndef function(x):\r\n time.sleep(1)\n return [x+2]\n "
},
"input_ports": {
"4:input:1": {
"id": "4:input:1",
"pos": [
-95.0,
-145.0
],
"contents": {},
"text": "x"
}
},
"output_ports": {
"4:output:1": {
"id": "4:output:1",
"pos": [
85.0,
-145.0
],
"contents": {},
"text": "x+2"
}
}
},
"5": {
"text": "process2:x=x*2",
"id": "5",
"pos": [
-120.0,
25.0
],
"icon": "E:\\Python\\pyminer_bin\\PyMiner\\bin\\pmgwidgets\\flowchart\\icons\\logo.png",
"content": {
"code": "\nimport time\ndef function(x):\r\n print(self)\r\n for i in range(100):\r\n self.signal_exec_doing.emit('doing')\r\n time.sleep(1)\n return [x*2]\n "
},
"input_ports": {
"5:input:1": {
"id": "5:input:1",
"pos": [
-115.0,
65.0
],
"contents": {},
"text": "x"
}
},
"output_ports": {
"5:output:1": {
"id": "5:output:1",
"pos": [
65.0,
65.0
],
"contents": {},
"text": "x*2"
}
}
}
},
"connections": [
{
"start_id": "1:output:1",
"end_id": "4:input:1",
"mid_positions": []
},
{
"start_id": "4:output:1",
"end_id": "3:input:1",
"mid_positions": []
},
{
"start_id": "1:output:1",
"end_id": "5:input:1",
"mid_positions": []
},
{
"start_id": "1:output:1",
"end_id": "3:input:2",
"mid_positions": []
},
{
"start_id": "5:output:1",
"end_id": "3:input:3",
"mid_positions": []
}
]
}

View File

@ -1,204 +0,0 @@
import json
import time
import copy
from PyQt5.QtCore import QObject, pyqtSignal
from typing import TYPE_CHECKING, Callable, List, Union, Tuple, Dict
if TYPE_CHECKING:
from pmgwidgets.flowchart.flow_node import Node
from pmgwidgets.flowchart.flowchart_widget import PMGraphicsScene
class FlowContentForFunction(QObject):
signal_exec_started = pyqtSignal(str)
signal_exec_doing = pyqtSignal(str)
signal_exec_finished = pyqtSignal(str)
signal_error_occurs = pyqtSignal(str)
def __init__(self, node):
super(FlowContentForFunction, self).__init__()
self.ports_changable = [False, False]
self.input_args = []
self.results = []
self.node: 'Node' = node
self.node.content = self
self.code: str = None
self.params: List[Union[Tuple, int, float, str]] = []
self.func: Callable = None
self.input_port_indices: List[List] = []
self.vars: Dict[str, Union[int, str, float, object]] = {}
def format_param(self) -> str:
keys = list(self.vars.keys())
if len(keys) == 1:
return str(self.vars[keys[0]])
else:
try:
return json.dumps(self.vars, indent=4)
except:
return str(self.vars)
def set_params(self, params: Union[str]):
try:
if isinstance(params, str):
params = eval(params)
if isinstance(params, list):
self.params = params
for i in range(len(params)):
self.params[i] = tuple(self.params[i])
param_name = self.params[i][1]
param_obj = self.params[i][3]
self.vars[param_name] = param_obj
self.node.display_internal_values(self.format_param())
except:
import traceback
traceback.print_exc()
def set_function(self, function_def: str = None, func_name: str = None):
import time
t1 = time.time()
self.code: str = function_def
g = globals()
g.update(self.vars)
exec(self.code, g)
self.func: Callable = globals().get(func_name)
t2 = time.time()
def refresh_input_port_indices(self):
input_port_list = [p.get_connected_port()[0] for p in self.node.input_ports if len(p.get_connected_port()) > 0]
self.input_port_indices = []
try:
for p in input_port_list:
index = p.node.get_port_index(p)
self.input_port_indices.append([p, index])
except:
import traceback
traceback.print_exc()
self.signal_error_occurs.emit('')
return
def get_settings_params(self):
return self.params
def update_settings(self, settings_dic: dict):
print(self.params)
# self.ports_changable[0]= settings_dic['inputs_changeable']
# self.ports_changable[1]= settings_dic['outputs_changeable']
for i, tup in enumerate(self.params):
param_name = tup[1]
l = list(self.params[i])
l[3] = settings_dic[param_name]
self.params[i] = tuple(l)
self.vars[param_name] = settings_dic[param_name]
self.node.display_internal_values(self.format_param())
def _process(self, input_args: list = None):
if input_args is None:
input_args = []
try:
for p, index in self.input_port_indices:
val = p.node.content.results[index]
val = copy.deepcopy(val)
input_args.append(val)
except:
import traceback
traceback.print_exc()
self.signal_error_occurs.emit('')
return
self.input_args = input_args
self.signal_exec_started.emit('started')
if not isinstance(input_args, list):
self.signal_error_occurs.emit('input list is not instance of list')
return None
t1 = time.time()
try:
result = self.process(*input_args)
except:
import traceback
exc = traceback.format_exc()
self.signal_error_occurs.emit('Module:%s\nInfo:%s' % (self.node.text, exc))
return None
self.results = copy.deepcopy(result)
next_content = self.get_next_content()
# self.signal_exec_finished.emit(
# 'finished\ninput value:%s\noutput value:%s' % (repr(input_list), repr(self.results)))
self.signal_exec_finished.emit('finished')
if next_content is None:
return
else:
next_content._process()
def dump(self):
return {'code': self.code, 'type': 'function', 'params': self.params, 'ports_changable': self.ports_changable}
def process(self, *args):
assert self.func is not None
globals().update(self.vars)
return self.func(*args)
def get_next_content(self) -> 'FlowContentForFunction':
scene: 'PMGraphicsScene' = self.node.base_rect.scene()
scene.node_index_to_execute += 1
node_index = scene.node_index_to_execute
if node_index >= len(scene.call_id_list):
scene.node_index_to_execute = 0
return None
node_id = scene.call_id_list[node_index]
return scene.find_node(node_id).content
class FlowContentEditableFunction(FlowContentForFunction):
def __init__(self, node, code: str = ''):
super().__init__(node)
self.ports_changable = [True, True]
self.input_args = []
self.results = []
self.node: 'Node' = node
self.node.content = self
if code == '':
code = """
import time
def function(x,y):
return y*2,x+2
"""
self.code = code
self.set_function(code, 'function')
def get_settings_params(self) -> List[Union[Tuple, List]]:
return [('text_edit', 'code', 'Input Python Code', self.code, 'python')]
def update_settings(self, settings_dic: dict):
self.code = settings_dic['code']
def process(self, *args):
globals().update({'self': self})
# exec(self.code, globals())
# return function(*args)
return self.func(*args)
def dump(self):
return {'code': self.code, 'type': 'custom_function'}
flowcontent_types = {'function': FlowContentForFunction,
'custom_function': FlowContentEditableFunction}
if __name__ == '__main__':
def process():
self = 123455
code = """
def function():
print(123)
"""
# locals().update({'self':self})
loc = locals()
exec(code)
print(loc['function'])
process()

Some files were not shown because too many files have changed in this diff Show More